# Ubiquiti Inform Protocol The mFi uses the Ubiquiti inform protocol to handle all communications to and from the controller. This is the way that it transmits the current state of the system to the controller (looks like it just sends mca-dump output) as well as how it receives the instructions from the controller as to what to enable or disable. Everything appears to be pull-based, even provisioning. This makes sense with the cloud controller where the controller has no access to your network. I have not documented yet how anything works within the protocol, that is next. This documents the overall protocol itself. The device will inform by executing an HTTP POST with an encrypted payload to http://controller:6080/inform at a regular interval (default is 10 seconds) and will expect an encrypted payload to be returned. If the device gets a command response instead of a noop response it will immediately do another inform; this continues until the controller sends the next noop response. Responses never appear to contain multiple commands. ## Raw Packet Structure | Size | Purpose | Data Type | | -------- | ------------------------- | --------- | | 4 bytes | magic number | integer | | 4 bytes | version | integer | | 6 bytes | mac address | string | | 2 bytes | flags | short | | 16 bytes | AES initialization vector | string | | 4 bytes | data version | integer | | 4 bytes | data length | integer | | n bytes | AES encrypted payload | string | ## Raw Packet Constraints * magic must == `1414414933` (TNBU) * data version must < `1` * `flags & 0x1 != 0` means encrypted * `flags & 0x2 != 0` means compressed ## Payload Types The payload is AES encrypted in CBC mode using PKCS5 padding. They key is the device auth key from the database or a master key that is hard coded if the device has not been provisioned yet. The master key is hard coded in the controller code in the DeviceManager class and pretty easy to find. MASTER_KEY = "ba86f2bbe107c7c57eb5f2690775c712" On devices running protocol version 1 the encrypted payload is just JSON data. In version 0 of the protocol the data was key=value pairs separated by newlines. All of the mFi hardware I have access to uses protocol version 1. The payloads break down into two categories; those coming into the controller and those going out of the controller. ## Output Payloads Output payloads are those that originate from the controller and are bound for the device. These always appear to contain a \_type field. I have observed the following output payloads. ### Firmware Upgrade _type: upgrade url: full url to firmware.bin datetime: rfc3339 formatted date, server time server_time_in_utc: server time in UTC as a unix timestamp (string) version: firmware version (string) time: server time as unix timestamp (int) _id: unknown id string (5232701de4b0457a2f2f031f) device_id: device ID from mongodb ### Config Update _type: setparam port_cfg: configuration for ports as string analog_cfg: analog port config (empty for mPower) authorized_guests: authorized guests file (empty) blocked_sta: blocked stations (empty) cfgversion: management config version mgmt_cfg: management config file port_cfg: output port config (set for mPower) system_cfg: system config file server_time_in_utc: server time in UTC as a unix timestamp (string) ### Reboot _type: reboot datetime: rfc3339 formatted date, server time device_id: device ID from mongodb server_time_in_utc: server time in UTC as a unix timestamp (string) time: server time as unix timestamp (int) _id: unknown id string (5232701de4b0457a2f2f031f) ### Heartbeat / No-Op _type: noop interval: next checkin time in seconds (integer) server_time_in_utc: server time in UTC as a unix timestamp (string) ### Locate Mode This mode didn't appear to change anything. My guess is that this should trigger the LED to blink as it does on the Unifi devices but appears to have no effect, at least on the mPower devices. _type: cmd cmd: locate datetime: rfc3339 formatted date, server time device_id: device ID from mongodb server_time_in_utc: server time in UTC as a unix timestamp (string) time: server time as unix timestamp (int) _id: unknown id string (5232701de4b0457a2f2f031f) ### Command _type: cmd _admin: admin data object _id: mongodb id of admin lang: admin language (en_US) name: admin username x_password: admin password _id: unknown id string (5232701de4b0457a2f2f031f) datetime: rfc3339 formatted date, server time server_time_in_utc: server time in UTC as a unix timestamp (string) time: server time as unix timestamp (int) device_id: device ID from mongodb cmd: command to use (mfi-output to change outputs) mac: device mac address model: device model (Outlet for mPower) off_volt: always 0?? (int) port: device port to update (int) sId: sensor ID timer: always 0?? (int) val: output value (int) volt: val and volt set to 1 to turn on, 0 to turn off (int) dimmer_ramp: always 1?? (int) (only for switch and outlet) ## Input Payloads Incoming packets appear to be a JSON version of the out put of the `mca-dump` command on the device. There is definitely some Unifi legacy in here. It appears that mFi is just using the Unfi firmware and has hacked it a bit for their use-case so most of the fields outside of alarm are not relevant to the mFi use-case. alarm: list of sensors index: port name sId: sensor ID hash time: device time // For mPort Only tag: kind of reading presented (magnetic, temperature, humidity) type: kind of device (input, analog, output) val: value (float) // For mPower Only entries: list of entry objects tag: kind of reading (output, pf, energy_sum, v_rms, i_rms, active_pwr) type: sensor type (output, analog, rmsSum, rms) val: value (float) if_table: list of interfaces and stats ip: interface ip mac: interface mac address name: interface device name (dev handle) rx_bytes: bytes received on the interface rx_dropped: packets dropped by the interface rx_errors: receive errors on the interface rx_packets: packets received on the interface tx_bytes: bytes transmitted by the interface tx_dropped: trasmit drops on the interface tx_errors: transmit errors on the interface tx_packets: number of packets transmitted by the interface type: appears to be the same as name radio_table: list of radios in the device builtin_ant_gain: gain of builtin antenna builtin_antenna: boolean, does device have antenna max_txpower: maximum transmit power name: name of radio radio: radio type (ex: ng) scan_table: list, unknown vap_table: table of joined wireless networks bssid: network SSID ccq: client connection qality channel: channel number essid: network friendly name id: mode? (ex: user) name: uplink device name num_sta: number of connected stations (always 0) radio: radio type (ex: ng) rx_bytes: bytes received on the interface rx_dropped: packets dropped by the interface rx_errors: receive errors on the interface rx_packets: packets received on the interface tx_bytes: bytes transmitted by the interface tx_dropped: trasmit drops on the interface tx_errors: transmit errors on the interface tx_packets: number of packets transmitted by the interface rx_crypts: unknown rx_frags: received fragmented packets rx_nwids: received network beacons tx_power: transmitting power of the radio (assumed in dBm) tx_retries: number of transmit retries on interface usage: same as id hostname: hostname of device ("ubnt" unless changed) ip: IP of device mac: mac address of primary interface mfi: boolean, indicates if an mfi device model: device model name model_display: display name for device serial: device serial number uptime: uptime in seconds since last reboot version: firmware version default: boolean, device is unconfigured cfgversion: string, unknown (ex: c3846443e1b4860b) guest_token: string, unknown (ex: 364E8B215D16AB963A53232E3873000C) inform_url: string, url to which the device is reporting isolated: boolean, can the device reach the rest of the network localversion: string, unknown (ex: ?) locating: boolean, is the device in locating mode (blinking LED) portversion: string, unknown (ex: 443eb55240f26367) time: integer, device time as unix timestamp trackable: boolean as string, unknown uplink: string, unix device name (dev handle) of the primary uplink device ## Config Samples These are some observed configuration payloads for the configuration packets. In their json form it is a single line string with newlines encoded as `\n`. ### mgmt cfg mgmt.is_default=false mgmt.authkey=41d6529fd555fbb1bdeeafeb995510fa mgmt.cfgversion=f1bb359840b519a4 mgmt.servers.1.url=http://172.16.0.38:6080/inform mgmt.selfrun_guest=pass selfrun_guest=pass cfgversion=f1bb359840b519a4 ### port cfg port.0.sensorId=52210822e4b0959e7fe94009 vpower.1.rep_output=1 vpower.1.rep_pf=1 vpower.1.rep_energy_sum=1 vpower.1.rep_v_rms=1 vpower.1.rep_i_rms=1 vpower.1.rep_active_pwr=1 vpower.1.relay=1 vpower.1.output_tag=output vpower.1.pf_tag=pf vpower.1.energy_sum_tag=energy_sum vpower.1.v_rms_tag=v_rms vpower.1.i_rms_tag=i_rms vpower.1.active_pwr_tag=active_pwr port.1.sensorId=5221082be4b0959e7fe9400a vpower.2.rep_output=1 vpower.2.rep_pf=1 vpower.2.rep_energy_sum=1 vpower.2.rep_v_rms=1 vpower.2.rep_i_rms=1 vpower.2.rep_active_pwr=1 vpower.2.relay=1 vpower.2.output_tag=output vpower.2.pf_tag=pf vpower.2.energy_sum_tag=energy_sum vpower.2.v_rms_tag=v_rms vpower.2.i_rms_tag=i_rms vpower.2.active_pwr_tag=active_pwr port.2.sensorId=5221083be4b0959e7fe9400b vpower.3.rep_output=1 vpower.3.rep_pf=1 vpower.3.rep_energy_sum=1 vpower.3.rep_v_rms=1 vpower.3.rep_i_rms=1 vpower.3.rep_active_pwr=1 vpower.3.relay=0 vpower.3.output_tag=output vpower.3.pf_tag=pf vpower.3.energy_sum_tag=energy_sum vpower.3.v_rms_tag=v_rms vpower.3.i_rms_tag=i_rms vpower.3.active_pwr_tag=active_pwr ### system cfg # users users.status=enabled users.1.name=admin users.1.password=Mq9xt5C8DjcLA users.1.status=enabled # bridge bridge.status=disabled bridge.1.devname=br0 bridge.1.fd=1 bridge.1.stp.status=disabled bridge.1.port.1.devname=eth1 snmp.status=disabled ppp.status=disabled pwdog.status=disabled dnsmasq.status=disabled dhcpd.status=disabled httpd.status=disabled httpd.port.http=80 httpd.port=80 igmpproxy.status=disabled telnetd.status=disabled tshaper.status=disabled netmode=bridge ntpclient.status=disabled ntpclient.1.server=pool.ntp.org ntpclient.1.status=disabled syslog.status=enabled resolv.status=enabled resolv.host.1.name=OfficePowerStrip resolv.nameserver.1.status=disabled resolv.nameserver.2.status=disabled dhcpc.status=enabled dhcpc.1.status=enabled dhcpc.1.devname=eth1 route.status=enabled vlan.status=disabled radio.1.ack.auto=disabled radio.1.ackdistance=300 radio.1.acktimeout=30 radio.1.ampdu.status=enabled radio.1.clksel=1 radio.1.countrycode=840 radio.1.cwm.enable=0 radio.1.cwm.mode=1 radio.1.forbiasauto=0 radio.1.channel=0 radio.1.ieee_mode=11nght40 radio.1.mcastrate=auto radio.1.mode=managed radio.1.puren=0 radio.1.rate.auto=enabled radio.1.rate.mcs=auto radio.1.txpower=auto # wlans (radio) radio.status=enabled radio.countrycode=840 aaa.status=disabled wireless.status=enabled dhcpc.2.status=enabled dhcpc.2.devname=ath0 bridge.1.port.2.devname=ath0 radio.1.devname=ath0 radio.1.status=enabled aaa.1.br.devname=br0 aaa.1.devname=ath0 aaa.1.driver=madwifi aaa.1.ssid= aaa.1.status=disabled wireless.1.mode=managed wireless.1.devname=ath0 wireless.1.status=enabled wireless.1.authmode=1 wireless.1.l2_isolation=disabled wireless.1.is_guest=false wireless.1.security=none wireless.1.addmtikie=disabled wireless.1.ssid= wireless.1.hide_ssid=enabled wireless.1.mac_acl.status=disabled wireless.1.mac_acl.policy=deny wireless.1.wmm=enabled # netconf netconf.status=enabled netconf.1.devname=eth1 netconf.1.autoip.status=disabled netconf.1.ip=0.0.0.0 netconf.1.promisc=enabled netconf.1.status=enabled netconf.1.up=enabled netconf.2.devname=br0 netconf.2.autoip.status=disabled netconf.2.ip=0.0.0.0 netconf.2.status=enabled netconf.2.up=enabled netconf.3.devname=ath0 netconf.3.autoip.status=disabled netconf.3.ip=0.0.0.0 netconf.3.promisc=enabled netconf.3.status=enabled netconf.3.up=enabled qos.status=enabled qos.group.1.rate=100 qos.group.2.rate=100 qos.group.6.rate=100 qos.if.1.devname=eth1 qos.if.1.devspeed=100 qos.if.1.group=1 qos.if.2.devname=ath0 qos.if.2.devspeed=150 qos.if.2.group=20