miio

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2026 License: Apache-2.0 Imports: 33 Imported by: 0

README

miio

Clients for Xiaomi Mi Home Wi-Fi devices in pure Go: the miIO local protocol, MIoT requests, specifications from miot-spec.org and the Mi account device list; not affiliated with Xiaomi.

Go Reference test Go

It talks miIO on UDP 54321 (token-encrypted JSON-RPC, the hello handshake and discovery), sends MIoT get_properties, set_properties and action requests, fetches a model's MIoT specification (or keeps a mirror of them for sites without internet, served in miot-spec.org's own shape) and maps it to names with value checks, finds devices by hello or through the application's mDNS browser, drives vacuums (with the Roborock consumables, history, schedule and settings), lights, plugs and air purifiers through one helper per family, reaches the Zigbee sub-devices of gateways, updates firmware, receives device pushes, and signs in to a Mi account (completing captcha and e-mail or SMS checks) to list its devices with their tokens and control them through the account. Standard library and lanbcast only, no cgo. It was written for SigmaM2M, a oneM2M platform, whose Xiaomi adapter is built on it.

Features

Feature Status
miIO packets, hello handshake, stamps, JSON-RPC with random request IDs and one retry yes
Hello discovery (broadcast to every LAN interface through lanbcast) yes
MIoT get_properties (batched by 15), set_properties, action, with per-property codes yes
MIoT specifications from miot-spec.org (newest released version per model; the full index with Status: "all"; fallback hosts) yes; none bundled
Offline specification mirror: a directory filled once from miot-spec.org (or carried in, or a site's own), served offline to the library and over HTTP in miot-spec.org's URL and JSON shape; the miiospec command fills and serves it yes (SpecMirror, cmd/miiospec)
Specification translations (the service's multiLanguage texts, by service, property, value and action) yes (Translations, TextKey); key shape from hass-xiaomi-miot, not checked against the live service
mDNS discovery (_miio._udp, model and device ID from the instance name) through the application's own mDNS browser yes (DiscoverMDNS, Browser)
Wi-Fi setup of a new device, MIoT probe, legacy get_prop in slices, typed device errors yes (ConfigureWiFi, SupportsMIoT, GetProp, DeviceError)
Mi account sign-in, homes, shared homes and device lists in every region yes
Mi account captcha and identity check (code by e-mail or SMS) completed with a person's help yes (VerifyError, Captcha, SendCode, VerifyCode); against the simulated account only
Specification mapped to names: properties and actions by "service.name", value list, range, step and access checked before sending yes (Model, opt-in)
Family helpers: vacuums, lights, plugs, air purifiers, through the specification or the legacy per-model methods (Roborock, Yeelight, Philips, chuangmi, zhimi) yes (Vacuum, Light, Plug, Purifier)
Roborock legacy extras: consumables and their reset, clean summary and records (both answer shapes), do-not-disturb, timers, time zone, voice volume, fan speed, carpet mode, find, spot yes; fan speed and find also through a MIoT specification, with fan presets from its value list
Roborock maps, rooms, zones and segments, mop and dock functions, sound packs not yet
Fan speed preset tables of legacy Roborock models not bundled: they are data of a GPL project; read and set the number (FanSpeed, SetFanSpeed)
Zigbee sub-devices of lumi gateways: list (both list methods), read, write, battery, unpair; sub-devices from the account yes (Gateway, GatewaySubDevices)
Firmware update: miIO.ota with URL and MD5, progress, state yes (Update, WaitUpdate)
Device pushes through local scenes (send_data_frame, miIO.xdel) yes (PushServer)
Device control through the Mi account (MIoT properties and actions, legacy methods relayed) yes (CloudGetProperties, CloudSetProperties, CloudDoAction, CloudCall); against the simulated account only
Bluetooth devices through the Mi account only (CloudGetProperties); no local method is known
Mi account scenes, statistics, firmware listing and other account APIs not yet [vendor account]: building them needs a real Mi account to learn and check the answers against
miot-spec.org's standard type definitions (/spec/services and the like) in the mirror not yet

Choosing a Go miIO library

Compared from each project's source at the version named, on 2026-10-12 (Go module proxy for the Go projects; PyPI and GitHub for the Python ones). Check the projects themselves before relying on a row. Newest versions found: python-miio 0.5.12 on PyPI (2022-07-18), with 0.6.0.dev0 (2024-03-13) and master at 9a00e08 (2026-07-25); micloud 0.6 (2022-12-08); hass-xiaomi-miot v1.1.5 (2026-09-07); mberatsanli/miio 2e4fd2a (2026-09-01), vkorn/go-miio e59e421 (2023-03-16) and nickw444/miio-go 379bc4c (2019-08-25), each unchanged since the last comparison. A search for newer Go code found only command-line tools (such as miullu/miiot-cli, 2026-06-30, with a specification cache of its own) and exporters, no library.

this module mberatsanli/miio (2e4fd2a) vkorn/go-miio (e59e421) nickw444/miio-go (379bc4c) python-miio master 9a00e08 (Python) hass-xiaomi-miot v1.1.5 (Python, Home Assistant)
miIO packets, handshake, discovery yes, and mDNS through the application's browser yes yes yes yes, and mDNS (zeroconf) yes
MIoT get/set properties, actions yes yes no no yes yes
MIoT specifications from miot-spec.org yes, with fallback hosts and translations no (a written-up spec for one model) no no yes yes, with translations and a second host
Specifications kept for offline use a mirror directory, served over HTTP in the service's shape no no no a cache that expires after hours Home Assistant storage, renewed after weeks
Mi account device and token list yes yes no no through micloud yes
Mi account e-mail verification completed yes, and SMS and captcha (simulated account only) yes no no no yes
Mi account scenes, statistics and the like no no no no no some
Specification mapped to names with value checks yes no no no yes (genericmiot) yes
Per-model helpers (vacuums, gateways, lights...) four families across MIoT and legacy models, Roborock consumables, history, schedule and settings, gateway sub-devices one Roborock model with maps and rooms Aqara gateway, sensors, vacuum Yeelight, plugs very many, with model quirks, Roborock maps and docks very many, by specification and per-model tweaks
Wi-Fi setup of a new device yes no no no yes no
Firmware update, device pushes yes no no no yes no
Device, account and specification simulators yes, separate module no no a device simulator miio and MIoT device simulators no
Interop evidence against python-miio both ways (below) none stated none stated none stated the reference none stated
Go version, dependencies 1.22; lanbcast 1.25; cobra, x/term (CLI) dep era, no go.mod 1.12; several n/a n/a
Licence Apache-2.0 MIT MIT MIT GPL-3.0 Apache-2.0

Pick this module for a Go protocol layer with MIoT, specification mapping and an offline specification mirror, the common device families with the Roborock legacy extras, gateways, firmware update, pushes and the account, with simulators for your tests. Pick mberatsanli/miio when you need its Roborock driver's maps and rooms, or a verification flow already used against real accounts (ours has run only against our simulator). Use python-miio outside Go, or when you need the per-model features of its many integrations (vacuum maps, docks, model-specific settings); use hass-xiaomi-miot inside Home Assistant, or for the account's scenes and statistics.

Install

go get github.com/cB-Sigma/miio@latest

Requires Go 1.22 or later.

Quick start

import "github.com/cB-Sigma/miio"

cloud := &miio.Cloud{Username: email, Password: password}
devs, err := cloud.Devices(ctx, []string{"i2"}) // India; miio.Regions lists all
tok, _ := hex.DecodeString(devs[0].Token)
c := &miio.Client{Addr: devs[0].LocalIP + ":54321", Token: tok, Timeout: 5 * time.Second}
info, err := c.Call(ctx, "miIO.info", nil)

// A MIoT device: read power (service 2, property 2) and switch it off.
res, err := miio.GetProperties(ctx, c, []miio.PropRef{{DID: devs[0].DID, SIID: 2, PIID: 2}})
_, err = miio.SetProperties(ctx, c, []miio.PropRef{{DID: devs[0].DID, SIID: 2, PIID: 2, Value: false}})

// The same by name, from the model's specification, and a family helper.
m, err := miio.ModelFor(ctx, &miio.HTTPSpecs{Base: miio.DefaultSpecURL, HTTP: http.DefaultClient}, devs[0].Model)
err = m.Set(ctx, c, devs[0].DID, "air-purifier.mode", "Sleep") // checked against the value list
p := &miio.Purifier{Family: miio.Family{Client: c, DID: devs[0].DID, Model: m}}
st, err := p.Status(ctx)

// A site without internet: fill a mirror once where there is internet,
// carry the directory, then read it offline (or serve m.Handler()).
m := &miio.SpecMirror{Dir: "/var/lib/miot-spec", Upstream: &miio.HTTPSpecs{Base: miio.DefaultSpecURL}}
err = m.Fill(ctx, "zhimi.airpurifier.ma4")
model, err := miio.ModelFor(ctx, &miio.SpecMirror{Dir: "/var/lib/miot-spec"}, "zhimi.airpurifier.ma4")

// When the account asks for a code at sign-in.
var ve *miio.VerifyError
if errors.As(cloud.Login(ctx), &ve) && ve.NotificationURL != "" {
	err = cloud.SendCode(ctx, miio.ByEmail)
	err = cloud.VerifyCode(ctx, miio.ByEmail, codeFromThePerson)
}

Example and ExampleGetProperties in example_test.go show the same (they need an account and a device, so they are not run); ExampleKeys and ExampleNewModel run. miio-sim runs them against simulated devices and a simulated account.

Documentation

API reference: https://pkg.go.dev/github.com/cB-Sigma/miio

Package What it does
miio (module root) miIO: Client (Call, DeviceID), Discover, Hello, Packet, Open, Keys. MIoT: GetProperties, SetProperties, DoAction, PropRef, PropResult, PropError; specifications: Spec, HTTPSpecs (Index, RawIndex, RawInstance, Translations, Fallbacks, Status), SpecInstance, SpecSource, SpecMirror (Fill, FillTranslations, FillAll, Add, AddTranslations, Models, Stored, URN, Handler), Translations, TextKey, ParseTranslations, URNName; names: Model (NewModel, ModelFor, Get, Set, SetMany, Do), Property.Check, Describe, ValueOf. Discovery and setup: DiscoverMDNS, Browser, BrowseFunc, MDNSEntry, MDNSDevice, ParseMDNSInstance, ConfigureWiFi, SupportsMIoT, GetProp, DeviceError. Families: Family, Vacuum (also Consumables, ResetConsumable, CleanSummary, CleanRecord, DND, SetDND, DisableDND, Timers, AddTimer, EnableTimer, DeleteTimer, Timezone, Volume, SetVolume, TestVolume, FanSpeed, SetFanSpeed, FanSpeedPresets, CarpetMode, SetCarpetMode, SetCarpetModeSettings, Find, Spot), Light, Plug, Purifier. Gateways: Gateway, SubDevice, GatewaySubDevices. Firmware: Update, UpdateProgress, UpdateState, WaitUpdate. Pushes: PushServer, EventInfo, Push, TokenEnc. Mi account: Cloud (Login, Devices, Captcha, SendCode, VerifyCode, CloudGetProperties, CloudSetProperties, CloudDoAction, CloudCall), VerifyError, CloudDevice, Regions
cmd/miiospec Fills a specification mirror directory from miot-spec.org (-fill models, -texts, -all n) and serves it (-listen :8080), offline with -upstream ""
interop (nested module, tests only) this module against python-miio's miio and MIoT device simulators and its packet code, in a container

Changes per version are in CHANGELOG.md.

Testing

go test -race ./...
cd interop && SIGMA_MIIO_INTEROP=1 go test -v ./...   # needs Docker

Tests carry // Covers: SIG-... comments: requirement IDs of the SigmaM2M project's compliance report, with no meaning outside it; the gateway, firmware, push and v0.3.0 tests claim none. The tests of the client, the families, gateways, firmware update, pushes and the account against the simulators are in miio-sim.

interop/ is a nested module with its own go.mod. Its tests build an image from interop/peer (python:3.12.10-slim with python-miio from a fixed commit, 9a00e08, of its master branch, which installs as 0.6.0.dev0: its simulators are not in a PyPI release yet) and are skipped unless SIGMA_MIIO_INTEROP=1 is set and Docker answers.

Interop tested against
  • v0.3.0: python-miio master 9a00e08 (0.6.0.dev0) with micloud 0.6, on python:3.12.10-slim, Docker Desktop on arm64, 2026-10-12 (interop; all six tests passed, the four earlier ones repeated):

    • miiocli devtools miio-simulator with the Roborock file python-miio ships (simulated_roborock.yaml, model roborock.vacuum.a15): Vacuum read the status, consumables, the clean summary in its list shape, a clean record in its object shape, do-not-disturb, the timers and the time zone as python-miio's simulator answers them; a method the file lacks came back as a device error. One observation, not a difference: the file's get_timer answer nests its second timer inside the first record, so it reads as one timer, in python-miio's own Timer as here.
    • python-miio's specification client (MiotCloud over micloud's MiotSpec, its base URL pointed at the mirror) against SpecMirror.Handler serving offline what Add put in: it picked the model's release from the index and built its pydantic device model from the specification (services and properties as sent).
    • Not covered by a peer: mDNS discovery (Docker Desktop does not pass multicast between the host and containers; DiscoverMDNS is tested with made-up browser answers and the simulator's names), the translations endpoint (python-miio does not read it), and the live miot-spec.org service.
  • python-miio master 9a00e08 (0.6.0.dev0) on python:3.12.10-slim, Docker Desktop on arm64, 2026-10-11 (interop; all passed):

    • miiocli devtools miio-simulator with the zhimi.fan file python-miio ships: hello handshake (device ID as python-miio derives it), miIO.info, get_prop, set_power and the changed value, an unknown method refused, a wrong token unanswered, discovery by hello.
    • miiocli devtools miot-simulator with a made-up specification (interop/peer/spec.json): get_properties with codes (an unknown property refused), set_properties and the values read back, a read-only property refused, an action. The same specification through Model: properties set and read by name, a mode set by its description, a value outside the list refused before sending, an action by name, the purifier helper's mode and status.
    • A push built with python-miio's own packet code (interop/peer/push.py) to PushServer: python-miio read the hello answer and the push's answer (result 0 under the server's device ID); the handler got the source and action.
    • The v0.1.0 run (python:3.12-slim) found that the simulator answers miIO.info without a "result" member; Client.Call returns such an answer whole, as python-miio does (TestAnswerWithoutResult).
  • python-miio's client (gateway, firmware update, vacuum with the Roborock extras, Wi-Fi setup and the MIoT probe, plug, push server) and micloud 0.6 against the simulators, in miio-sim.

  • Not yet [hardware]: a real Xiaomi device, gateway or sub-device; the family helpers against real vacuums, lights, plugs and purifiers; firmware update with a vendor image (a wrong image can break a device); pushes from a real device's scene (which trigger strings a model accepts is found from the vendor app's own scenes).

  • Not yet [vendor account]: the captcha and e-mail or SMS check, device control through the account and the device list against a real Mi account; the live miot-spec.org service (the mirror, fallbacks and translations have run only against made-up services).

  • Not yet [hardware]: the Roborock extras and Wi-Fi setup against a real vacuum or a device in setup mode; mDNS names announced by real devices.

  • miio-sim: simulated miIO and MIoT devices, gateways with sub-devices, firmware update and pushes, a simulated Mi account (with its verification steps) and specification service, as a Go package and a command.
  • lanbcast: the broadcast helper discovery uses.

Status

v0.x: the API may change before v1.

Contributing

See CONTRIBUTING.md.

Security

See SECURITY.md to report a vulnerability privately.

Licence

Apache-2.0 (see LICENSE and NOTICE). Copyright 2026 Abhinav Gautam. Xiaomi, Mi, Mi Home and MIoT are trademarks of their owner; the names are used only to say which devices and services this module talks to.

Documentation

Overview

Package miio speaks the local protocol of Xiaomi Mi Home Wi-Fi devices: miIO packets on UDP port 54321 with the token-derived encryption (Client, Discover), the legacy JSON-RPC methods, the MIoT property and action requests (GetProperties, SetProperties, DoAction) with the models' MIoT specifications (HTTPSpecs, or SpecMirror for sites without internet, with their translations) mapped to names (Model), discovery by mDNS through the application's browser (DiscoverMDNS), helpers for vacuums, lights, plugs and air purifiers (Vacuum, Light, Plug, Purifier; the vacuum also with the Roborock consumables, history, schedule and settings), Wi-Fi setup (ConfigureWiFi), gateway sub-devices (Gateway), firmware update (Update), device pushes (PushServer), and the Mi account client that lists an account's devices with their tokens, completes its verification steps and controls devices through the account (Cloud).

Xiaomi does not document the local protocol or the account API; this package follows public descriptions (see PROVENANCE.md). The module github.com/cB-Sigma/miio-sim simulates devices, gateways, the account and the specification service for tests.

Example

List the account's devices with their tokens, then switch a plug on its LAN. It needs a Mi account and a device, so it is not run; github.com/cB-Sigma/miio-sim runs the same steps against a simulated account and plug.

package main

import (
	"context"
	"encoding/hex"
	"fmt"
	"time"

	"github.com/cB-Sigma/miio"
)

func main() {
	ctx := context.Background()
	cloud := &miio.Cloud{Username: "user@example.invalid", Password: "secret"}
	devs, err := cloud.Devices(ctx, []string{"i2"}) // India; miio.Regions lists all
	if err != nil || len(devs) == 0 {
		fmt.Println(devs, err)
		return
	}
	tok, _ := hex.DecodeString(devs[0].Token)
	c := &miio.Client{Addr: devs[0].LocalIP + ":54321", Token: tok, Timeout: 3 * time.Second}
	_, err = c.Call(ctx, "set_power", []any{"on"})
	fmt.Println(devs[0].Model, err)
}

Index

Examples

Constants

View Source
const (
	ByEmail = "email"
	ByPhone = "phone"
)

Verification ways for SendCode and VerifyCode.

View Source
const (
	OTAIdle        = "idle"
	OTADownloading = "downloading"
	OTAInstalling  = "installing"
	OTAFailed      = "failed"
)

OTA states.

View Source
const DefaultSpecURL = "https://miot-spec.org/miot-spec-v2"

DefaultSpecURL is miot-spec.org's specification service.

View Source
const MDNSService = "_miio._udp"

MDNSService is the miIO mDNS service type.

View Source
const Port = 54321

Port is miIO's UDP port.

View Source
const SpecMirrorURL = "https://spec.miot-spec.com/miot-spec-v2"

SpecMirrorURL is a second public host of the same service, for HTTPSpecs.Fallbacks.

Variables

View Source
var (
	// ErrCloudAuth: the account refused the user name or password.
	ErrCloudAuth = errors.New("the Mi account refused the user name or password")
	// ErrCloudVerify: Xiaomi asks for a captcha or an e-mail or SMS code.
	// The error is a *VerifyError; Captcha, CaptchaCode, SendCode and
	// VerifyCode complete the sign-in with a person's help.
	ErrCloudVerify = errors.New("the Mi account asks for a verification (a captcha or a code sent by e-mail or SMS)")
)

Errors of the sign-in.

View Source
var (
	// ErrUnknownName: the specification has no such property or action, or
	// the short name is shared by several.
	ErrUnknownName = errors.New("no such property or action in the MIoT specification")
	// ErrValue: a value does not fit the property's format, range or list.
	ErrValue = errors.New("the value does not fit the property")
	// ErrAccess: the property cannot be read or written.
	ErrAccess = errors.New("the property does not allow this access")
)

Errors of the specification mapping.

View Source
var ErrNoSpec = errors.New("no MIoT specification of the model")

ErrNoSpec means no specification of the model could be found.

View Source
var ErrNotMirrored = errors.New("not in the MIoT specification mirror")

ErrNotMirrored means the mirror has no copy and no upstream to fetch it from (or the upstream failed).

View Source
var ErrOTAFailed = errors.New("the device reports the firmware update as failed")

ErrOTAFailed means the device reported the update as failed.

View Source
var ErrToken = errors.New("the device's answer does not open with the token")

ErrToken means the device's answer could not be opened with the token.

View Source
var ErrUnsupported = errors.New("the device model has no such function")

ErrUnsupported means the device model has no such function.

View Source
var Regions = []string{"cn", "de", "us", "ru", "tw", "sg", "in", "i2"}

Regions are the Mi account's server regions (cn has no prefix; i2 is India's).

Functions

func CheckAction

func CheckAction(raw json.RawMessage) error

CheckAction reads an action answer and returns a *PropError when its code is not 0.

func ConfigureWiFi added in v0.3.0

func ConfigureWiFi(ctx context.Context, c *Client, ssid, password string, uid int64, extra map[string]any) error

ConfigureWiFi tells a device to join a Wi-Fi network (miIO.config_router with ssid, passwd and uid; extra adds further parameters some models take). It is sent to a device in setup mode, on its own access point (usually 192.168.13.1:54321); the device leaves that network at once, so a missing answer is not proof of failure.

func Decrypt

func Decrypt(token, ct []byte) ([]byte, error)

Decrypt reverses Encrypt.

func DefaultAPIBase

func DefaultAPIBase(region string) string

DefaultAPIBase is the Mi account's API address of a region.

func Discover

func Discover(ctx context.Context, bcast string, wait time.Duration) (map[uint32]string, error)

Discover broadcasts a hello and returns device ID → address.

func DoAction

func DoAction(ctx context.Context, c *Client, did string, siid, aiid int, in []any) error

DoAction runs an action of a service with its input values.

func EncSignature

func EncSignature(method, path string, params [][2]string, signedNonce string) string

EncSignature is the SHA-1 signature over the method, the path, the parameters in order and the signed nonce.

func Encrypt

func Encrypt(token, plain []byte) []byte

Encrypt encrypts data for a token.

func GetProp added in v0.3.0

func GetProp(ctx context.Context, c *Client, keys []string, max int) ([]any, error)

GetProp reads legacy properties with get_prop, at most max at a time (devices that cannot take a long list need a few at a time; max < 1 asks for all at once). The values come in the order of keys.

func Hello

func Hello() []byte

Hello is the discovery and handshake packet.

func Keys

func Keys(token []byte) (key, iv []byte)

Keys derives the AES key and IV from a 16-byte token.

Example
package main

import (
	"encoding/hex"
	"fmt"

	"github.com/cB-Sigma/miio"
)

func main() {
	tok, _ := hex.DecodeString("00112233445566778899aabbccddeeff")
	key, iv := miio.Keys(tok)
	fmt.Println(len(key), len(iv))
}
Output:
16 16

func Packet

func Packet(token []byte, deviceID, stamp uint32, plain []byte) []byte

Packet builds a packet with encrypted data.

func ParseMDNSInstance added in v0.3.0

func ParseMDNSInstance(name string) (model string, did uint32, ok bool)

ParseMDNSInstance reads the model and device ID from a miIO instance name ("yeelink-light-color1_miio12345678", optionally followed by "._miio._udp.local."). ok is false for a name without "_miio"; did is 0 when no number follows it.

func RC4Decrypt

func RC4Decrypt(signedNonce, v string) ([]byte, error)

RC4Decrypt reverses RC4Encrypt.

func RC4Encrypt

func RC4Encrypt(signedNonce, v string) string

RC4Encrypt encrypts a value with a signed nonce (base64 in and out).

func SignedNonce

func SignedNonce(ssecurity, nonce string) string

SignedNonce is the per-call key: SHA-256(ssecurity ‖ nonce), base64.

func SubDeviceModel added in v0.2.0

func SubDeviceModel(typeID int) string

SubDeviceModel names the zigbee model of a type number ("" when not known).

func SupportsMIoT added in v0.3.0

func SupportsMIoT(ctx context.Context, c *Client) (bool, error)

SupportsMIoT tells whether the device answers MIoT get_properties (it asks for one property, service 1 property 1). A device that refuses the method speaks only the legacy methods; err is set only when the device could not be asked.

func TextKey added in v0.3.0

func TextKey(siid, piid, aiid, value int) string

TextKey is the translation key of a service (piid, aiid and value all below 0), of one of its properties (piid ≥ 0), of a value of that property's value list (value ≥ 0: its position in the list, from 0), or of an action (aiid ≥ 0).

func TokenEnc added in v0.2.0

func TokenEnc(token []byte) string

TokenEnc is how a scene target carries the target's token: the first 16 bytes of the token encrypted with itself, in hex.

func URNName

func URNName(urn string) (string, bool)

URNName is the name part of a MIoT type URN ("urn:miot-spec-v2:property:on:00000006:..." gives "on") and whether it is a standard (miot-spec-v2) one.

func Update added in v0.2.0

func Update(ctx context.Context, c *Client, url, md5Hex string) error

Update asks the device to download the image at url, check it against md5Hex (32 hex digits) and install it.

func UpdateProgress added in v0.2.0

func UpdateProgress(ctx context.Context, c *Client) (int, error)

UpdateProgress reads the download progress in per cent.

func UpdateState added in v0.2.0

func UpdateState(ctx context.Context, c *Client) (string, error)

UpdateState reads the update state (OTAIdle, OTADownloading, OTAInstalling, OTAFailed, or another word a device uses).

func WaitUpdate added in v0.2.0

func WaitUpdate(ctx context.Context, c *Client, interval time.Duration, progress func(state string, pct int)) error

WaitUpdate polls the state every interval until the device is idle again after an update (nil), reports failure (ErrOTAFailed) or ctx ends. A device that restarts to install stops answering for a while; such silence is waited out. progress, when not nil, receives each reading.

Types

type Action

type Action struct {
	IID         int    `json:"iid"`
	Type        string `json:"type"`
	Description string `json:"description,omitempty"`
	In          []int  `json:"in,omitempty"`
	Out         []int  `json:"out,omitempty"`
}

Action is one action of a service.

type BrowseFunc added in v0.3.0

type BrowseFunc func(ctx context.Context, service string) ([]MDNSEntry, error)

BrowseFunc makes a function a Browser.

func (BrowseFunc) Browse added in v0.3.0

func (f BrowseFunc) Browse(ctx context.Context, service string) ([]MDNSEntry, error)

Browse implements Browser.

type Browser added in v0.3.0

type Browser interface {
	Browse(ctx context.Context, service string) ([]MDNSEntry, error)
}

Browser browses mDNS for a service type ("_miio._udp"), returning the instances found before ctx ends or its own wait is over.

type CarpetMode added in v0.3.0

type CarpetMode struct {
	Enabled                                             bool
	StallTime, CurrentLow, CurrentHigh, CurrentIntegral int
}

CarpetMode is the vacuum's carpet detection: when on, it raises suction on carpet, detected by the brush motor's current crossing the thresholds (the units are the firmware's own).

type CleanRecord added in v0.3.0

type CleanRecord struct {
	Start, End time.Time
	Duration   time.Duration
	Area       float64 // m²
	Error      int     // 0: none
	Complete   bool
}

CleanRecord is one cleaning run.

type CleanSummary added in v0.3.0

type CleanSummary struct {
	Duration time.Duration
	Area     float64 // m²
	Count    int
	// Records are the IDs of the stored runs (start times in Unix
	// seconds), newest first, for CleanRecord.
	Records []int64
}

CleanSummary is the vacuum's cleaning history in total.

type Client

type Client struct {
	Addr    string // host:port
	Token   []byte
	Timeout time.Duration
	// contains filtered or unexported fields
}

Client talks to one device.

func (*Client) Call

func (c *Client) Call(ctx context.Context, method string, params any) (json.RawMessage, error)

Call sends a JSON-RPC request and returns its result, or the whole answer when it has neither "result" nor "error". Request IDs start at a random value (devices ignore an ID they answered recently, so a restarted client must not begin at 1 again); when a request is not answered, the IDs move on by 100 and it is asked once more.

func (*Client) DeviceID

func (c *Client) DeviceID() uint32

DeviceID is the ID the device gave in its last answer.

type Cloud

type Cloud struct {
	Username, Password string
	// AccountBase is https://account.xiaomi.com (tests change it).
	AccountBase string
	// APIBase gives a region's API address
	// (https://<region>.api.io.mi.com/app).
	APIBase func(region string) string
	HTTP    *http.Client
	// CaptchaCode is the text of the captcha image (Captcha) a person
	// read; the next Login sends it.
	CaptchaCode string
	// contains filtered or unexported fields
}

Cloud signs in to a Mi account and lists its devices.

func (*Cloud) Captcha added in v0.2.0

func (c *Cloud) Captcha(ctx context.Context) ([]byte, error)

Captcha fetches the captcha image the last Login was asked for.

func (*Cloud) CloudCall added in v0.2.0

func (c *Cloud) CloudCall(ctx context.Context, region, did, method string, params any) (json.RawMessage, error)

CloudCall relays a legacy miIO method to a device through the account and returns the device's result.

func (*Cloud) CloudDoAction added in v0.2.0

func (c *Cloud) CloudDoAction(ctx context.Context, region, did string, siid, aiid int, in []any) error

CloudDoAction runs a MIoT action through the account.

func (*Cloud) CloudGetProperties added in v0.2.0

func (c *Cloud) CloudGetProperties(ctx context.Context, region string, refs []PropRef) ([]PropResult, error)

CloudGetProperties reads MIoT properties through the account.

func (*Cloud) CloudSetProperties added in v0.2.0

func (c *Cloud) CloudSetProperties(ctx context.Context, region string, refs []PropRef) ([]PropResult, error)

CloudSetProperties writes MIoT properties through the account and returns a *PropError for the first one refused.

func (*Cloud) Devices

func (c *Cloud) Devices(ctx context.Context, regions []string) ([]CloudDevice, error)

Devices signs in when needed and lists the account's devices in the regions (every region when empty), with the homes shared with it.

func (*Cloud) Login

func (c *Cloud) Login(ctx context.Context) error

Login signs in (steps 1 to 3).

func (*Cloud) SendCode added in v0.2.0

func (c *Cloud) SendCode(ctx context.Context, by string) error

SendCode asks the account to send a verification code by e-mail (ByEmail) or SMS (ByPhone), after a Login that answered a VerifyError with a NotificationURL.

func (*Cloud) VerifyCode added in v0.2.0

func (c *Cloud) VerifyCode(ctx context.Context, by, code string) error

VerifyCode completes the sign-in with the code a person received (by the same way given to SendCode). Devices and later calls then work.

type CloudDevice

type CloudDevice struct {
	DID     string `json:"did"`
	Token   string `json:"token"`
	Name    string `json:"name"`
	Model   string `json:"model"`
	LocalIP string `json:"localip"`
	MAC     string `json:"mac"`
	Online  bool   `json:"isOnline"`
	Region  string `json:"region,omitempty"`
	// ParentID and ParentModel name the gateway of a sub-device.
	ParentID    string `json:"parent_id,omitempty"`
	ParentModel string `json:"parent_model,omitempty"`
}

CloudDevice is one device of the account.

func GatewaySubDevices added in v0.2.0

func GatewaySubDevices(devs []CloudDevice, gatewayDID string) []CloudDevice

GatewaySubDevices picks from an account's device list the sub-devices whose parent is the gateway (for gateways that do not list them locally).

type Consumable added in v0.3.0

type Consumable string

Consumable names a part whose use the vacuum counts.

const (
	MainBrush     Consumable = "main_brush_work_time"
	SideBrush     Consumable = "side_brush_work_time"
	Filter        Consumable = "filter_work_time"
	Sensors       Consumable = "sensor_dirty_time"
	CleaningBrush Consumable = "cleaning_brush_work_times" // a counter of uses, on docks that wash
	Strainer      Consumable = "strainer_work_times"       // a counter of uses, on docks that wash
)

The parts reset_consumable takes; each is also the key of its counter in get_consumable's answer.

type Consumables added in v0.3.0

type Consumables struct {
	MainBrush, SideBrush, Filter, Sensors time.Duration
	// Raw is the whole answer, with counters of models that have more
	// parts.
	Raw map[string]any
}

Consumables are how long each part has been used since its last reset.

type DND added in v0.3.0

type DND struct {
	Enabled                                    bool
	StartHour, StartMinute, EndHour, EndMinute int
}

DND is the vacuum's do-not-disturb period, in its own time zone.

type DeviceError added in v0.3.0

type DeviceError struct {
	Method  string
	Code    int
	Message string
}

DeviceError is an error answer of the device to a request (such as -32601 for a method it does not know).

func (*DeviceError) Error added in v0.3.0

func (e *DeviceError) Error() string

type EventInfo added in v0.2.0

type EventInfo struct {
	// Action is the name the pushes arrive under (our choice).
	Action string
	// Extra identifies the trigger, such as "[1,6,1,0,[0,1],2,0]".
	Extra string
	// Event is the event name in the trigger; Action when empty.
	Event string
	// CommandExtra is passed back with each push.
	CommandExtra string
	// TriggerValue is a threshold, for triggers that have one.
	TriggerValue any
	// TriggerToken is needed by protected triggers (a gateway's alarm).
	TriggerToken string
	// SourceDID and SourceModel name the source; the watched device when
	// empty. Gateway sub-devices are named by their sid and zigbee model.
	SourceDID, SourceModel string
}

EventInfo describes one event to subscribe to.

type Family added in v0.2.0

type Family struct {
	Client      *Client
	DID         string
	Model       *Model // nil: legacy methods
	LegacyModel string // such as "yeelink.light.color1"; used when Model is nil
}

Family is what the helpers share: the client, the device ID used in MIoT requests, and either a Model or, for legacy devices, the model name.

type Gateway added in v0.2.0

type Gateway struct {
	Client *Client
	// Model is the gateway's model; "lumi.gateway.mgl03" lists with
	// get_device_list, the others with the device_list property.
	Model string
}

Gateway reaches a gateway's sub-devices.

func (*Gateway) RemoveSubDevice added in v0.2.0

func (g *Gateway) RemoveSubDevice(ctx context.Context, sid string) error

RemoveSubDevice unpairs a sub-device from the gateway.

func (*Gateway) SetSubProp added in v0.2.0

func (g *Gateway) SetSubProp(ctx context.Context, sid, name string, v any) error

SetSubProp writes a property of a sub-device.

func (*Gateway) SubBattery added in v0.2.0

func (g *Gateway) SubBattery(ctx context.Context, sid string) (int, error)

SubBattery reads a battery-powered sub-device's battery level, per cent (older gateways; the multi-mode gateway reports "voltage" instead).

func (*Gateway) SubDevices added in v0.2.0

func (g *Gateway) SubDevices(ctx context.Context) ([]SubDevice, error)

SubDevices lists the gateway's sub-devices, without the gateway itself.

func (*Gateway) SubProp added in v0.2.0

func (g *Gateway) SubProp(ctx context.Context, sid, name string) (any, error)

SubProp reads one property of a sub-device with get_device_prop.

func (*Gateway) SubProps added in v0.2.0

func (g *Gateway) SubProps(ctx context.Context, sid string, names ...string) ([]any, error)

SubProps reads properties of a sub-device ("temperature", "humidity", "voltage"...) in one get_device_prop_exp request, in order.

type HTTPSpecs

type HTTPSpecs struct {
	Base string
	HTTP *http.Client
	// Fallbacks are further bases tried in order when Base cannot be
	// reached or answers a server error (such as SpecMirrorURL).
	Fallbacks []string
	// Status is the index asked for: "released" when empty, or "all" for
	// every version (a released version is then still preferred).
	Status string
	// contains filtered or unexported fields
}

HTTPSpecs fetches specifications from a specification service.

func (*HTTPSpecs) Index added in v0.3.0

func (s *HTTPSpecs) Index(ctx context.Context) ([]SpecInstance, error)

Index returns the service's index.

func (*HTTPSpecs) RawIndex added in v0.3.0

func (s *HTTPSpecs) RawIndex(ctx context.Context) ([]byte, error)

RawIndex returns the service's index as it answered it.

func (*HTTPSpecs) RawInstance added in v0.3.0

func (s *HTTPSpecs) RawInstance(ctx context.Context, urn string) ([]byte, error)

RawInstance returns the specification with this URN as the service answered it.

func (*HTTPSpecs) RawTranslations added in v0.3.0

func (s *HTTPSpecs) RawTranslations(ctx context.Context, urn string) ([]byte, error)

RawTranslations returns the texts of the specification with this URN in other languages, as the service answered them (/instance/v2/multiLanguage, beside /miot-spec-v2 on the same host).

func (*HTTPSpecs) Spec

func (s *HTTPSpecs) Spec(ctx context.Context, model string) (*Spec, error)

Spec implements SpecSource.

func (*HTTPSpecs) Translations added in v0.3.0

func (s *HTTPSpecs) Translations(ctx context.Context, urn string) (Translations, error)

Translations returns the texts of the specification with this URN in other languages.

type Header struct {
	Length   uint16
	Unknown  uint32
	DeviceID uint32
	Stamp    uint32
	Checksum []byte
}

Header is a parsed packet header.

func Open

func Open(token, b []byte) (Header, []byte, error)

Open checks a packet's checksum and decrypts its data.

func ParseHeader

func ParseHeader(b []byte) (Header, error)

ParseHeader reads a packet's header.

type Light added in v0.2.0

type Light struct{ Family }

Light is a lamp or bulb.

func (*Light) Off added in v0.2.0

func (l *Light) Off(ctx context.Context) error

Off switches the light off.

func (*Light) On added in v0.2.0

func (l *Light) On(ctx context.Context) error

On switches the light on.

func (*Light) SetBrightness added in v0.2.0

func (l *Light) SetBrightness(ctx context.Context, pct int) error

SetBrightness sets the brightness in per cent.

func (*Light) SetColor added in v0.2.0

func (l *Light) SetColor(ctx context.Context, rgb int) error

SetColor sets the colour as 0xRRGGBB.

func (*Light) SetColorTemperature added in v0.2.0

func (l *Light) SetColorTemperature(ctx context.Context, kelvin int) error

SetColorTemperature sets the colour temperature in kelvin (MIoT and Yeelight). Philips legacy lights take a 1-100 scale instead: use SetColorTemperatureScale.

func (*Light) SetColorTemperatureScale added in v0.2.0

func (l *Light) SetColorTemperatureScale(ctx context.Context, v int) error

SetColorTemperatureScale sets a Philips legacy light's colour temperature on its 1-100 scale.

func (*Light) Status added in v0.2.0

func (l *Light) Status(ctx context.Context) (LightStatus, error)

Status reads the light's state.

type LightStatus added in v0.2.0

type LightStatus struct {
	On               bool
	Brightness       int // per cent
	ColorTemperature int // kelvin (MIoT and Yeelight) or the Philips 1-100 scale
	Color            int // 0xRRGGBB
}

LightStatus is a light's state; fields the model lacks stay 0.

type MDNSDevice added in v0.3.0

type MDNSDevice struct {
	Model    string // such as "zhimi.airpurifier.m1"
	DID      uint32 // 0 when the name does not carry it
	Addr     string // host:port for Client.Addr
	Instance string
}

MDNSDevice is a device found by mDNS.

func DiscoverMDNS added in v0.3.0

func DiscoverMDNS(ctx context.Context, b Browser) ([]MDNSDevice, error)

DiscoverMDNS browses for "_miio._udp" with the application's browser and returns the miIO devices among the answers, one per instance, with an IPv4 address preferred. A device found this way still needs its token; Client's first call does the hello.

type MDNSEntry added in v0.3.0

type MDNSEntry struct {
	Instance string // the instance name, with or without the service and domain after it
	IPs      []net.IP
	Port     int // 0: the miIO port, 54321
}

MDNSEntry is one service instance a browser found.

type MappedAction added in v0.2.0

type MappedAction struct {
	Name string // "<service>.<action>"
	SIID int
	Action
}

MappedAction is one action of a Model with its full name.

type MappedProperty added in v0.2.0

type MappedProperty struct {
	Name string // "<service>.<property>"
	SIID int
	Property
}

MappedProperty is one property of a Model with its full name.

type Model added in v0.2.0

type Model struct {
	Spec *Spec
	// contains filtered or unexported fields
}

Model maps a specification's names to IDs. Build it with NewModel; it is safe for concurrent use once built.

func ModelFor added in v0.2.0

func ModelFor(ctx context.Context, specs SpecSource, model string) (*Model, error)

ModelFor fetches a model's specification and maps it.

func NewModel added in v0.2.0

func NewModel(sp *Spec) (*Model, error)

NewModel builds the mapping of a specification.

Example

Map a specification to names and check values before sending them.

package main

import (
	"fmt"

	"github.com/cB-Sigma/miio"
)

func main() {
	m, _ := miio.NewModel(&miio.Spec{Type: "urn:miot-spec-v2:device:light:0000A001:demo-lamp:1", Services: []miio.Service{
		{IID: 2, Type: "urn:miot-spec-v2:service:light:00007802:demo-lamp:1", Properties: []miio.Property{
			{IID: 2, Type: "urn:miot-spec-v2:property:brightness:0000000D:demo-lamp:1", Format: "uint8",
				Access: []string{"read", "write"}, ValueRange: []float64{1, 100, 1}}}}}})
	p, _ := m.Property("light.brightness")
	_, err := p.Check(150)
	fmt.Println(p.SIID, p.IID, err != nil)
}
Output:
2 2 true

func (*Model) Action added in v0.2.0

func (m *Model) Action(name string) (MappedAction, error)

Action finds an action by full name, unique short name or "<siid>.<aiid>".

func (*Model) Actions added in v0.2.0

func (m *Model) Actions() []MappedAction

Actions lists the actions in specification order.

func (*Model) Do added in v0.2.0

func (m *Model) Do(ctx context.Context, c *Client, did, name string, in ...any) error

Do runs an action by name with its inputs, checking their number and, where the inputs are properties of the same service, their values.

func (*Model) Get added in v0.2.0

func (m *Model) Get(ctx context.Context, c *Client, did string, names ...string) (map[string]any, error)

Get reads properties by name and returns their values by the names given. Values of enumerated properties stay numbers; Describe names them. A property the device refused is left out and reported as a *PropError (the first one) next to the values it did give.

func (*Model) Has added in v0.2.0

func (m *Model) Has(name string) bool

Has reports whether a property or action of that name exists.

func (*Model) Properties added in v0.2.0

func (m *Model) Properties() []MappedProperty

Properties lists the properties in specification order.

func (*Model) Property added in v0.2.0

func (m *Model) Property(name string) (MappedProperty, error)

Property finds a property by full name, unique short name or "<siid>.<piid>".

func (*Model) Set added in v0.2.0

func (m *Model) Set(ctx context.Context, c *Client, did, name string, v any) error

Set checks and writes one property.

func (*Model) SetMany added in v0.2.0

func (m *Model) SetMany(ctx context.Context, c *Client, did string, vals map[string]any) error

SetMany checks every value first and writes them in one request. A value for an enumerated property may be the number or its description (for example "Sleep" for a mode).

type Plug added in v0.2.0

type Plug struct{ Family }

Plug is a smart plug or outlet.

func (*Plug) Off added in v0.2.0

func (p *Plug) Off(ctx context.Context) error

Off switches the plug off.

func (*Plug) On added in v0.2.0

func (p *Plug) On(ctx context.Context) error

On switches the plug on.

func (*Plug) Status added in v0.2.0

func (p *Plug) Status(ctx context.Context) (PlugStatus, error)

Status reads the plug's state.

type PlugStatus added in v0.2.0

type PlugStatus struct {
	On          bool
	Temperature float64 // °C, 0 when not reported
}

PlugStatus is a plug's state.

type PropError

type PropError struct {
	SIID, IID int // IID is the property or action ID
	Code      int
	Action    bool
}

PropError is a set_properties or action answer with a non-zero code.

func (*PropError) Error

func (e *PropError) Error() string

Error tells which property or action was refused and the code.

type PropRef

type PropRef struct {
	DID   string `json:"did"`
	SIID  int    `json:"siid"`
	PIID  int    `json:"piid"`
	Value any    `json:"value,omitempty"`
	Code  int    `json:"code,omitempty"`
}

PropRef names one property in a MIoT request.

type PropResult

type PropResult struct {
	DID   string `json:"did"`
	SIID  int    `json:"siid"`
	PIID  int    `json:"piid"`
	Code  int    `json:"code"`
	Value any    `json:"value"`
}

PropResult is one property of a get_properties or set_properties answer, or the answer to an action. Code 0 is success; devices answer -4001 to -4005 and others for a property they lack, cannot write or cannot take the value of.

func CheckSet

func CheckSet(raw json.RawMessage) ([]PropResult, error)

CheckSet reads a set_properties answer and returns a *PropError for the first property answered with a non-zero code.

func GetProperties

func GetProperties(ctx context.Context, c *Client, refs []PropRef) ([]PropResult, error)

GetProperties reads properties, in requests of at most 15, and returns one result per property in order. A property the device lacks has a non-zero Code.

Example

Read a MIoT device's properties by service and property ID. It needs a device, so it is not run.

package main

import (
	"context"
	"encoding/hex"
	"fmt"
	"time"

	"github.com/cB-Sigma/miio"
)

func main() {
	tok, _ := hex.DecodeString("0f1e2d3c4b5a69788796a5b4c3d2e1f0")
	c := &miio.Client{Addr: "192.168.1.70:54321", Token: tok, Timeout: 3 * time.Second}
	res, err := miio.GetProperties(context.Background(), c, []miio.PropRef{
		{DID: "purifier", SIID: 2, PIID: 2}, // power
		{DID: "purifier", SIID: 3, PIID: 6}, // PM2.5
	})
	if err != nil {
		fmt.Println(err)
		return
	}
	for _, r := range res {
		fmt.Println(r.SIID, r.PIID, r.Code, r.Value)
	}
}

func SetProperties

func SetProperties(ctx context.Context, c *Client, refs []PropRef) ([]PropResult, error)

SetProperties writes properties (each PropRef with its Value) in one request. It returns the results, and a *PropError for the first property answered with a non-zero code.

type Property

type Property struct {
	IID         int         `json:"iid"`
	Type        string      `json:"type"`
	Description string      `json:"description,omitempty"`
	Format      string      `json:"format,omitempty"`
	Access      []string    `json:"access,omitempty"`
	Unit        string      `json:"unit,omitempty"`
	ValueRange  []float64   `json:"value-range,omitempty"`
	ValueList   []ValueItem `json:"value-list,omitempty"`
}

Property is one property of a service.

func (Property) Can

func (p Property) Can(a string) bool

Can reports whether the property allows an access: "read", "write" or "notify".

func (Property) Check added in v0.2.0

func (p Property) Check(v any) (any, error)

Check tests a value against the property's format, value range (min, max, step) and value list, and returns it in the form to send: bool, int64 for integer formats, float64 for float, string for string. It takes numbers of any Go kind, json.Number, "on"/"off" and "true"/"false" for bool, and a value-list description for an enumerated property.

func (Property) Describe added in v0.2.0

func (p Property) Describe(v any) string

Describe names a value of an enumerated property by its description ("" when the property has no value list or the value is not in it).

func (Property) ValueOf added in v0.2.0

func (p Property) ValueOf(desc string) (int, bool)

ValueOf finds a value of an enumerated property by its description, ignoring case, spaces, hyphens and underscores.

type Purifier added in v0.2.0

type Purifier struct{ Family }

Purifier is an air purifier.

func (*Purifier) Off added in v0.2.0

func (p *Purifier) Off(ctx context.Context) error

Off switches the purifier off.

func (*Purifier) On added in v0.2.0

func (p *Purifier) On(ctx context.Context) error

On switches the purifier on.

func (*Purifier) SetFanLevel added in v0.2.0

func (p *Purifier) SetFanLevel(ctx context.Context, level int) error

SetFanLevel sets the fan level (MIoT only).

func (*Purifier) SetMode added in v0.2.0

func (p *Purifier) SetMode(ctx context.Context, mode string) error

SetMode sets the mode by name: a description from the specification's value list for MIoT ("Auto", "Sleep", "Favorite"), the legacy method's word otherwise ("auto", "silent", "favorite").

func (*Purifier) Status added in v0.2.0

func (p *Purifier) Status(ctx context.Context) (PurifierStatus, error)

Status reads the purifier's state.

type PurifierStatus added in v0.2.0

type PurifierStatus struct {
	On          bool
	Mode        string // the mode's description (MIoT) or name (legacy)
	FanLevel    int
	PM25        float64 // µg/m³ (the legacy "aqi" reading)
	Humidity    float64 // per cent
	Temperature float64 // °C
}

PurifierStatus is a purifier's state; fields the model lacks stay 0.

type Push added in v0.2.0

type Push struct {
	Device string          // the sender's IP address
	Source string          // the source's ID (a device ID or a sub-device sid)
	Action string          // EventInfo.Action
	Params json.RawMessage // what the device sent with it
}

Push is one event a device sent.

type PushServer added in v0.2.0

type PushServer struct {
	// Addr is where it listens; ":54321" when empty. Devices always send to
	// port 54321 of ServerIP.
	Addr string
	// DeviceID and Model are what the listener poses as; 100000001 and
	// chuangmi.plug.v3 (a model devices accept as a scene target) when
	// zero.
	DeviceID uint32
	Model    string
	// ServerIP is the address devices send to; when empty, the local
	// address that routes to each device.
	ServerIP string
	// contains filtered or unexported fields
}

PushServer receives device pushes.

func (*PushServer) Close added in v0.2.0

func (s *PushServer) Close() error

Close stops listening. Scenes stay on the devices: Unsubscribe them first.

func (*PushServer) LocalAddr added in v0.2.0

func (s *PushServer) LocalAddr() net.Addr

LocalAddr is the listening address.

func (*PushServer) Register added in v0.2.0

func (s *PushServer) Register(c *Client, handler func(Push)) error

Register accepts pushes from the client's device and hands them to handler (on the server's goroutine: return quickly).

func (*PushServer) Start added in v0.2.0

func (s *PushServer) Start() error

Start listens.

func (*PushServer) Subscribe added in v0.2.0

func (s *PushServer) Subscribe(ctx context.Context, c *Client, ev EventInfo) (string, error)

Subscribe installs a scene on the client's device that pushes the event to this server, and returns the scene's ID. The device must be Registered.

func (*PushServer) Subscriptions added in v0.2.0

func (s *PushServer) Subscriptions(c *Client) []string

Subscriptions lists the scene IDs installed through this server on a device.

func (*PushServer) Unsubscribe added in v0.2.0

func (s *PushServer) Unsubscribe(ctx context.Context, c *Client, id string) error

Unsubscribe removes a scene from the client's device.

type Service

type Service struct {
	IID         int        `json:"iid"`
	Type        string     `json:"type"`
	Description string     `json:"description,omitempty"`
	Properties  []Property `json:"properties,omitempty"`
	Actions     []Action   `json:"actions,omitempty"`
}

Service is one service of a specification.

type Spec

type Spec struct {
	Type        string    `json:"type"`
	Description string    `json:"description,omitempty"`
	Services    []Service `json:"services"`
}

Spec is a model's MIoT specification, as miot-spec.org serves it.

type SpecInstance added in v0.3.0

type SpecInstance struct {
	Model   string `json:"model"`
	Version int    `json:"version"`
	Type    string `json:"type"`             // the specification's URN
	Status  string `json:"status,omitempty"` // "released" and others; only in the full index
	TS      int64  `json:"ts,omitempty"`
}

SpecInstance is one entry of the service's index: a version of a model's specification.

type SpecMirror added in v0.3.0

type SpecMirror struct {
	Dir string
	// Upstream fills the mirror: the index when there is none (or it is
	// older than MaxAge), and each specification or translation on first
	// request. nil: offline, only what the directory holds.
	Upstream *HTTPSpecs
	// MaxAge is how old the index may grow before Upstream is asked for
	// a new one; 0: the index is kept until Fill is called. An unknown
	// model also asks Upstream for a new index, at most once an hour.
	MaxAge time.Duration
	// contains filtered or unexported fields
}

SpecMirror keeps MIoT specifications in a local directory, for sites without internet or with unreliable internet. Filled from the specification service once (Fill, or on first use when Upstream is set), it serves from the directory from then on, also when the service cannot be reached. It is a SpecSource, and Handler serves the directory in the service's own URL and JSON shape, so HTTPSpecs (and other clients of miot-spec.org) can be pointed at it.

The directory holds instances.json (the service's index as it answered it), instance/<URN>.json and multiLanguage/<URN>.json (one specification and its translations each, as the service answered them; the URN query-escaped). Files are written whole through a temporary file and a rename, so a reader never sees half a file.

func (*SpecMirror) Add added in v0.3.0

func (m *SpecMirror) Add(model string, version int, raw []byte) error

Add stores a specification under a model and version (its URN is the specification's "type") and lists it in the index: for specifications carried in by hand, or a site's own. A model's newest version is the one used.

func (*SpecMirror) AddTranslations added in v0.3.0

func (m *SpecMirror) AddTranslations(urn string, raw []byte) error

AddTranslations stores the translations of the specification with this URN, in the service's answer shape.

func (*SpecMirror) Fill added in v0.3.0

func (m *SpecMirror) Fill(ctx context.Context, models ...string) error

Fill fetches a new index from Upstream and the newest specification of each model named (already stored ones are kept); with no models, only the index.

func (*SpecMirror) FillAll added in v0.3.0

func (m *SpecMirror) FillAll(ctx context.Context, workers int) error

FillAll fetches the newest specification of every model in the index that is not stored yet, with up to workers requests at a time (1 when below 1). It goes on past a failed model and returns the first error with the number of failures. The full index has many thousand models: expect a long run and a large directory.

func (*SpecMirror) FillTranslations added in v0.3.0

func (m *SpecMirror) FillTranslations(ctx context.Context, models ...string) error

FillTranslations fetches the translations of each model's newest specification (already stored ones are kept).

func (*SpecMirror) Handler added in v0.3.0

func (m *SpecMirror) Handler() http.Handler

Handler serves the mirror in the specification service's shape: a path ending in /instances answers the index (filtered by ?status= when its entries carry a status), /instance?type=<URN> a specification, and /multiLanguage?urn=<URN> its translations. Mounted at the root of a server, HTTPSpecs{Base: "http://<server>/miot-spec-v2"} reads it. With Upstream set, what is missing is fetched through and kept.

func (*SpecMirror) Models added in v0.3.0

func (m *SpecMirror) Models() []string

Models lists the models in the mirror's index, sorted.

func (*SpecMirror) RawInstance added in v0.3.0

func (m *SpecMirror) RawInstance(ctx context.Context, urn string) ([]byte, error)

RawInstance returns the stored specification with this URN, fetching it once when Upstream is set.

func (*SpecMirror) RawTranslations added in v0.3.0

func (m *SpecMirror) RawTranslations(ctx context.Context, urn string) ([]byte, error)

RawTranslations returns the stored translations of the specification with this URN, fetching them once when Upstream is set.

func (*SpecMirror) Spec added in v0.3.0

func (m *SpecMirror) Spec(ctx context.Context, model string) (*Spec, error)

Spec implements SpecSource.

func (*SpecMirror) Stored added in v0.3.0

func (m *SpecMirror) Stored(model string) bool

Stored tells whether the model's newest specification is in the directory.

func (*SpecMirror) Translations added in v0.3.0

func (m *SpecMirror) Translations(ctx context.Context, urn string) (Translations, error)

Translations returns the translations of the specification with this URN.

func (*SpecMirror) URN added in v0.3.0

func (m *SpecMirror) URN(ctx context.Context, model string) (string, error)

URN returns the URN of the model's newest specification in the mirror.

type SpecSource

type SpecSource interface {
	Spec(ctx context.Context, model string) (*Spec, error)
}

SpecSource gives a model's MIoT specification.

type SubDevice added in v0.2.0

type SubDevice struct {
	SID      string // "lumi.158d..."
	TypeID   int    // the gateway's type number; -1 when listed by model
	Model    string // zigbee model ("lumi.sensor_ht.v1"), from the list or from TypeID
	Firmware int    // -1 when not listed
}

SubDevice is a sub-device of a gateway.

type Translations added in v0.3.0

type Translations map[string]map[string]string

Translations are a specification's texts in other languages: language (such as "zh_cn" or "en") → text key → text. The keys name a service, a property, a property's value-list entry or an action by their IIDs: TextKey builds them.

func ParseTranslations added in v0.3.0

func ParseTranslations(b []byte) (Translations, error)

ParseTranslations reads the service's answer to a translations request ({"type": ..., "data": {language: {key: text}}}).

func (Translations) Text added in v0.3.0

func (t Translations) Text(lang, key string) string

Text returns the text of key in lang, or "" when there is none.

type Vacuum added in v0.2.0

type Vacuum struct{ Family }

Vacuum is a robot vacuum.

func (*Vacuum) AddTimer added in v0.3.0

func (v *Vacuum) AddTimer(ctx context.Context, id, cron string) (string, error)

AddTimer schedules a cleaning by a cron schedule in the vacuum's time zone (legacy) and returns its ID; id "" takes the current time in Unix milliseconds, as the vendor app does. The vacuum adds it enabled on most firmware.

func (*Vacuum) CarpetMode added in v0.3.0

func (v *Vacuum) CarpetMode(ctx context.Context) (CarpetMode, error)

CarpetMode reads the carpet detection settings (legacy).

func (*Vacuum) CleanRecord added in v0.3.0

func (v *Vacuum) CleanRecord(ctx context.Context, id int64) (CleanRecord, error)

CleanRecord reads one run by its ID from CleanSummary.Records (legacy). Older firmware answers a list (start, end, duration, area, error, complete), newer an object; both are read.

func (*Vacuum) CleanSummary added in v0.3.0

func (v *Vacuum) CleanSummary(ctx context.Context) (CleanSummary, error)

CleanSummary reads the cleaning history (legacy). Older firmware answers a list (time, area, count, record IDs), newer an object with the same named fields; both are read.

func (*Vacuum) Consumables added in v0.3.0

func (v *Vacuum) Consumables(ctx context.Context) (Consumables, error)

Consumables reads the use of the brushes, filter and sensors (legacy).

func (*Vacuum) DND added in v0.3.0

func (v *Vacuum) DND(ctx context.Context) (DND, error)

DND reads the do-not-disturb period (legacy).

func (*Vacuum) DeleteTimer added in v0.3.0

func (v *Vacuum) DeleteTimer(ctx context.Context, id string) error

DeleteTimer removes a scheduled cleaning (legacy).

func (*Vacuum) DisableDND added in v0.3.0

func (v *Vacuum) DisableDND(ctx context.Context) error

DisableDND turns the do-not-disturb period off (legacy).

func (*Vacuum) EnableTimer added in v0.3.0

func (v *Vacuum) EnableTimer(ctx context.Context, id string, on bool) error

EnableTimer turns a scheduled cleaning on or off (legacy).

func (*Vacuum) FanSpeed added in v0.3.0

func (v *Vacuum) FanSpeed(ctx context.Context) (int, error)

FanSpeed reads the suction setting: the legacy custom mode (a per cent on early models, a preset number on later ones), or the MIoT fan level's value.

func (*Vacuum) FanSpeedPresets added in v0.3.0

func (v *Vacuum) FanSpeedPresets() (map[string]int, error)

FanSpeedPresets names the suction settings of a MIoT vacuum, from its specification's value list (description → value). Legacy models take numbers that differ by model and firmware; this module bundles no table of them, so it answers ErrUnsupported there: read the current one with FanSpeed.

func (*Vacuum) Find added in v0.3.0

func (v *Vacuum) Find(ctx context.Context) error

Find makes the vacuum say where it is: the legacy find_me, or the MIoT identify action.

func (*Vacuum) Home added in v0.2.0

func (v *Vacuum) Home(ctx context.Context) error

Home sends the vacuum back to its dock.

func (*Vacuum) Pause added in v0.2.0

func (v *Vacuum) Pause(ctx context.Context) error

Pause pauses cleaning.

func (*Vacuum) ResetConsumable added in v0.3.0

func (v *Vacuum) ResetConsumable(ctx context.Context, part Consumable) error

ResetConsumable sets a part's counter back to zero after it was replaced or cleaned (legacy).

func (*Vacuum) SetCarpetMode added in v0.3.0

func (v *Vacuum) SetCarpetMode(ctx context.Context, on bool) error

SetCarpetMode turns carpet detection on or off, keeping the vacuum's thresholds (it reads them first) (legacy).

func (*Vacuum) SetCarpetModeSettings added in v0.3.0

func (v *Vacuum) SetCarpetModeSettings(ctx context.Context, c CarpetMode) error

SetCarpetModeSettings writes all carpet detection settings (legacy).

func (*Vacuum) SetDND added in v0.3.0

func (v *Vacuum) SetDND(ctx context.Context, startHour, startMinute, endHour, endMinute int) error

SetDND sets and enables the do-not-disturb period (legacy).

func (*Vacuum) SetFanSpeed added in v0.3.0

func (v *Vacuum) SetFanSpeed(ctx context.Context, speed int) error

SetFanSpeed sets the suction setting: a number FanSpeed reads or FanSpeedPresets lists.

func (*Vacuum) SetVolume added in v0.3.0

func (v *Vacuum) SetVolume(ctx context.Context, pct int) error

SetVolume sets the voice volume in per cent (legacy).

func (*Vacuum) Spot added in v0.3.0

func (v *Vacuum) Spot(ctx context.Context) error

Spot cleans a small area around the vacuum (legacy).

func (*Vacuum) Start added in v0.2.0

func (v *Vacuum) Start(ctx context.Context) error

Start starts cleaning.

func (*Vacuum) Status added in v0.2.0

func (v *Vacuum) Status(ctx context.Context) (VacuumStatus, error)

Status reads the state and battery.

func (*Vacuum) Stop added in v0.2.0

func (v *Vacuum) Stop(ctx context.Context) error

Stop stops cleaning.

func (*Vacuum) TestVolume added in v0.3.0

func (v *Vacuum) TestVolume(ctx context.Context) error

TestVolume plays a sound at the current volume (legacy).

func (*Vacuum) Timers added in v0.3.0

func (v *Vacuum) Timers(ctx context.Context) ([]VacuumTimer, error)

Timers lists the scheduled cleanings (legacy).

func (*Vacuum) Timezone added in v0.3.0

func (v *Vacuum) Timezone(ctx context.Context) (string, error)

Timezone reads the vacuum's time zone, such as "Asia/Kolkata" (legacy); timers and the do-not-disturb period follow it.

func (*Vacuum) Volume added in v0.3.0

func (v *Vacuum) Volume(ctx context.Context) (int, error)

Volume reads the voice volume in per cent (legacy).

type VacuumStatus added in v0.2.0

type VacuumStatus struct {
	// Code is the state number: MIoT's vacuum status value, or the legacy
	// Roborock state code.
	Code int
	// State names it: the specification's description (MIoT) or our own
	// name for the common Roborock codes; "" when unknown.
	State   string
	Battery int // per cent, -1 when not reported
}

VacuumStatus is a vacuum's state.

type VacuumTimer added in v0.3.0

type VacuumTimer struct {
	ID      string // the creation time in Unix milliseconds, as text
	Enabled bool
	// Cron is the schedule, "minute hour day-of-month month day-of-week"
	// in the vacuum's time zone (VacuumTimezone).
	Cron string
	// Action is what runs, as the vacuum reports it (such as
	// "start_clean"), with its parameters.
	Action string
	Params any
}

VacuumTimer is a scheduled cleaning.

type ValueItem

type ValueItem struct {
	Value       int    `json:"value"`
	Description string `json:"description"`
}

ValueItem is one allowed value of an enumerated property.

type VerifyError added in v0.2.0

type VerifyError struct {
	// CaptchaURL is set when a captcha must be read.
	CaptchaURL string
	// WrongCaptcha: the CaptchaCode sent was refused.
	WrongCaptcha bool
	// NotificationURL is set when a code must be sent and entered.
	NotificationURL string
}

VerifyError is ErrCloudVerify with what the account asked for.

func (*VerifyError) Error added in v0.2.0

func (e *VerifyError) Error() string

func (*VerifyError) Unwrap added in v0.2.0

func (e *VerifyError) Unwrap() error

Unwrap makes errors.Is(err, ErrCloudVerify) true.

Directories

Path Synopsis
cmd
miiospec command
Command miiospec keeps a local mirror of MIoT specifications and serves it in the shape of miot-spec.org, for sites without internet:
Command miiospec keeps a local mirror of MIoT specifications and serves it in the shape of miot-spec.org, for sites without internet:

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL