Coder Social home page Coder Social logo

jens-maus / node-unifi Goto Github PK

View Code? Open in Web Editor NEW
131.0 10.0 48.0 1.72 MB

NodeJS class for querying/controlling a UniFi-Controller (www.ubnt.com)

License: MIT License

JavaScript 100.00%
nodejs unifi javascript ubiquiti unifi-controller cloud-key udm udm-pro

node-unifi's Introduction


node-unifi

Build NPM version Downloads License Donate GitHub stars

Node-UniFi is a NodeJS module that allows to query/control UniFi devices via the official UniFi-Controller API. It is developed to be compatible to the UniFi-Controller API version starting with v4.x.x up to v8.x.x

Features

  • Support all UniFi-Controller API features introduced with v4.x.x up to v8.x.x.
  • Support CloudKey Gen1, CloudKey Gen2, UnifiOS-based UDM-Pro Controller as well as self-hostd UniFi controller software.
  • Returns all data in well-defined JSON parsable strings/objects.
  • Use of modern axios-based nodejs http library.
  • API functions returning NodeJS Promises for modern nodejs uses via async/await or then()/catch().
  • Support for WebSocket-based push notifications of UniFi controllers for listening for state/object changes using EventEmitter-based nodejs functionality.
  • Usable with local and UniFI cloud accounts and with 2FA authentication.

Requirements

  • Installed UniFi-Controller/Network version v4 up to v8 (UDM-Pro, UDM-SE, UDM, UDR, UDW, CloudKey Gen1/Gen2).
  • Direct network connectivity between the application using node-unifi and the host:port (normally TCP port 443 or 8443) where the UniFi controller is running on.
  • Node.js version >= 16.x

Installation

node-unifi can be installed using the following npm command:

npm install node-unifi

Examples

node-unifi has been designed to be used quite straight forward and without introducing ackward language constructs. The following example should give a brief introduction on how to use node-unifi in your own applications using its Promises-based API interface:

const Unifi = require('node-unifi');
const unifi = new Unifi.Controller({'<HOSTNAME>', '<PORT>', sslverify: false});

(async () => {
  try {
    // LOGIN
    const loginData = await unifi.login('<USERNAME>', '<PASSWORD>');
    console.log('login: ' + loginData);

    // GET SITE STATS
    const sites = await unifi.getSitesStats();
    console.log('getSitesStats: ' + sites[0].name + ':' + sites.length);
    console.log(JSON.stringify(sites));

    // GET SITE SYSINFO
    const sysinfo = await unifi.getSiteSysinfo();
    console.log('getSiteSysinfo: ' + sysinfo.length);
    console.log(JSON.stringify(sysinfo));

    // GET CLIENT DEVICES
    const clientData = await unifi.getClientDevices();
    console.log('getClientDevices: ' + clientData.length);
    console.log(JSON.stringify(clientData));

    // GET ALL USERS EVER CONNECTED
    const usersData = await unifi.getAllUsers();
    console.log('getAllUsers: ' + usersData.length);
    console.log(JSON.stringify(usersData));

    // LOGOUT
    const logoutData = await unifi.logout();
    console.log('logout: ' + JSON.stringify(logoutData));
  } catch (error) {
    console.log('ERROR: ' + error);
  }
})();

Please note that every unifi.XXXXX() function returns a Promise, thus async/await as well as .then()/.catch() can be used accordingly.

Event-Emitter WebSockets Interface

Since version 2.0.0 node-unifi supports (thanks to unifi-axios-events) the WebSocket interface of a UniFi controller. This new interface allows to listen for events using unifi.listen() and automatically receive events as soon as the UniFi controller sends them out via its WebSocket functionality. For receiving these events in a nodejs-compatible way node-unifi uses internally EventEmitter2 which allows to execute actions based on event filters defined by unifi.on(...).

An example on how to use this EventEmitter-based functionality of node-unifi to immediately receive state changes rather than regularly having to poll a unifi controller for changes can be seen here:

const Unifi = require('node-unifi');
const unifi = new Unifi.Controller({'<HOSTNAME>', '<PORT>', sslverify: false});

(async () => {
  try {
    // LOGIN
    const loginData = await unifi.login('<USERNAME>', '<PASSWORD>');
    console.log('login: ' + loginData);

    // LISTEN for WebSocket events
    const listenData = await unifi.listen();
    console.log('listen: ' + listenData);

    // Listen for alert.client_connected
    unifi.on('alert.client_connected', function (data) {
      const ts = new Date(data[0].timestamp).toISOString();
      console.log(`${ts} - ${this.event}: ${data[0].parameters.CLIENT.id} (${data[0].parameters.CLIENT.name})`);
    });

    // Listen for alert.client_disconnected
    unifi.on('alert.client_disconnected', function (data) {
      const ts = new Date(data[0].timestamp).toISOString();
      console.log(`${ts} - ${this.event}: ${data[0].parameters.CLIENT.id} (${data[0].parameters.CLIENT.name})`);
    });

    // Listen for ctrl.* events
    unifi.on('ctrl.*', function () {
      console.log(`${this.event}`);
    });
  } catch (error) {
    console.log('ERROR: ' + error);
  }
})();

More examples can be found in the "examples" sub-directory of this GitHub repository.

Moving from v1 of node-unifi to v2+

If you are having an application still using the obsolete v1 version of node-unifi and you want to port it to using the new/revised v2 version, all you have to do is:

  • make sure your application can deal with NodeJS Promises as all node-unifi API functions return proper Promises allowing to use async/await or .then()/.catch() logic for synchronous processing of events (see Examples) rather than expecting callback functions, forcing you to nest them properly.
  • eliminate the previously necessary site function argument required when calling a node-unifi function. Now you can either use the { site: 'my site' } argument when passing contructor options to node-unifi or you switch to a different site using unifi.opts.site='my site' before calling a node-unifi API function.
  • as the API functions had been changed to work on a single site only, make sure your app is changed so that it expects a single site JSON return dataset only.
  • The new version by default verifies SSL connections and certificates. To restore the behaviour of the old version set sslverify: false in the constructor options

References

This nodejs package/class uses functionality/Know-How gathered from different third-party projects:

Use-Cases

The following projects are known to use this nodejs class for query/control UniFi devices:

License

The MIT License (MIT)

Copyright (c) 2017-2023 Jens Maus <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

node-unifi's People

Contributors

apollon77 avatar dependabot[bot] avatar dersimn avatar greenkeeper[bot] avatar jacobalberty avatar jens-maus avatar julusian avatar mabunixda avatar nicorodrigues avatar philthurston avatar ramwolken9 avatar ryangannon avatar scrounger avatar snyk-bot avatar tkopczuk avatar vishal1503 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

node-unifi's Issues

403 error on new 7.1.61 Unifi Network on cloud key Gen2 and Invalid CSRF Token

ERROR startListening:  Error: Request failed with status code 403
at createError (/var/www/html/plugins/unifi/resources/node_modules/node-unifi/node_modules/axios/lib/core/createError.js:16:15)
at settle (/var/www/html/plugins/unifi/resources/node_modules/node-unifi/node_modules/axios/lib/core/settle.js:17:12)
at /var/www/html/plugins/unifi/resources/node_modules/node-unifi/node_modules/axios-cookiejar-support/lib/interceptors/response.js:83:25
at new Promise ()
at responseInterceptor (/var/www/html/plugins/unifi/resources/node_modules/node-unifi/node_modules/axios-cookiejar-support/lib/interceptors/response.js:82:9)
at /var/www/html/plugins/unifi/resources/node_modules/node-unifi/node_modules/axios-cookiejar-support/lib/index.js:130:67
at processTicksAndRejections (internal/process/task_queues.js:95:5) {
config: {
url: 'https://192.168.1.8/proxy/network/dl/firmware/bundles.json',
method: 'post',
data: '{}',
headers: {
Accept: 'application/json, text/plain, */*',
'X-CSRF-Token': 'aaaaaaa-9306-4bdf-9b45-151134d8c7df',
'Content-Type': 'application/json',
Cookie: 'TOKEN=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.eyJ1c2VySWQiOiIwNWE5YWJiMi1hYmE1LTQ1YjMtYTc2MS0wOWJlYjQ2YWU2NDEiLCJjc3JmVG9rZW4iOiIwYjdhZDU2ZS1hYmU5LTQ3N2MtYjRjNS0wNDllZTU4MzQ4ZTUiLCJqdGkiOiIwZmFkN2E5Ni1hYmNkLTQ3YjAtODZmNy1iYjEzMWM5MjYxODYiLCJwYXNzd29yZFJldmlzaW9uIjoxNjEyMzEwNjQ2LCJpYXQiOjE2NTIyMjE1MDgsImV4cCI6MTY1MjIyNTEwOH0.bWzGkzRyb51_PGuZsa7oexvP9f-8ORtdNfqNcnky8BM',
'User-Agent': 'axios/0.21.4',
'Content-Length': 2
},
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
withCredentials: true,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
httpsAgent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 443,
protocol: 'https:',
options: [Object],
requests: {},
sockets: {},
freeSockets: [Object],
keepAliveMsecs: 1000,
keepAlive: true,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 0,
maxCachedSessions: 100,
_sessionCache: [Object],
[Symbol(kCapture)]: false
},
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
jar: CookieJar {
rejectPublicSuffixes: true,
enableLooseMode: false,
allowSpecialUseDomain: false,
store: { idx: {
'192.168.1.8': {
'/': {
TOKEN: Cookie="TOKEN=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.eyJ1c2VySWQiOiIwNWE5YWJiMi1hYmE1LTQ1YjMtYTc2MS0wOWJlYjQ2YWU2NDEiLCJjc3JmVG9rZW4iOiIwYjdhZDU2ZS1hYmU5LTQ3N2MtYjRjNS0wNDllZTU4MzQ4ZTUiLCJqdGkiOiIwZmFkN2E5Ni1hYmNkLTQ3YjAtODZmNy1iYjEzMWM5MjYxODYiLCJwYXNzd29yZFJldmlzaW9uIjoxNjEyMzEwNjQ2LCJpYXQiOjE2NTIyMjE1MDgsImV4cCI6MTY1MjIyNTEwOH0.bWzGkzRyb51_PGuZsa7oexvP9f-8ORtdNfqNcnky8BM; Path=/; Secure; HttpOnly; hostOnly=true; aAge=10ms; cAge=22ms"
}
}
} },
prefixSecurity: 'silent',
_cloneSync: [Function (anonymous)],
_importCookiesSync: [Function (anonymous)],
getCookiesSync: [Function (anonymous)],
getCookieStringSync: [Function (anonymous)],
getSetCookieStringsSync: [Function (anonymous)],
removeAllCookiesSync: [Function (anonymous)],
setCookieSync: [Function (anonymous)],
serializeSync: [Function (anonymous)]
},
maxRedirects: 0,
validateStatus: [Function: validateStatus]
},
request:  ClientRequest {
_events: [Object: null prototype] {
error: [Function: handleRequestError],
prefinish: [Function: requestOnPrefinish]
},
_eventsCount: 2,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: true,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: true,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 2,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: TLSSocket {
_tlsOptions: [Object],
_secureEstablished: true,
_securePending: false,
_newSessionPending: false,
_controlReleased: true,
secureConnecting: false,
_SNICallback: null,
servername: false,
alpnProtocol: false,
authorized: false,
authorizationError: 'DEPTH_ZERO_SELF_SIGNED_CERT',
encrypted: true,
_events: [Object: null prototype],
_eventsCount: 9,
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_readableState: [ReadableState],
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: undefined,
_server: null,
ssl: [TLSWrap],
_requestCert: true,
_rejectUnauthorized: false,
parser: null,
_httpMessage: null,
timeout: 0,
[Symbol(res)]: [TLSWrap],
[Symbol(verified)]: true,
[Symbol(pendingSession)]: null,
[Symbol(async_id_symbol)]: -1,
[Symbol(kHandle)]: [TLSWrap],
[Symbol(kSetNoDelay)]: false,
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(connect-options)]: [Object],
[Symbol(RequestTimeout)]: undefined
},
_header: 'POST /proxy/network/dl/firmware/bundles.json HTTP/1.1
' +
'Accept: application/json, text/plain, */*
' +
'X-CSRF-Token: aaaaaaa-9306-4bdf-9b45-151134d8c7df
' +
'Content-Type: application/json
' +
'Cookie: TOKEN=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.eyJ1c2VySWQiOiIwNWE5YWJiMi1hYmE1LTQ1YjMtYTc2MS0wOWJlYjQ2YWU2NDEiLCJjc3JmVG9rZW4iOiIwYjdhZDU2ZS1hYmU5LTQ3N2MtYjRjNS0wNDllZTU4MzQ4ZTUiLCJqdGkiOiIwZmFkN2E5Ni1hYmNkLTQ3YjAtODZmNy1iYjEzMWM5MjYxODYiLCJwYXNzd29yZFJldmlzaW9uIjoxNjEyMzEwNjQ2LCJpYXQiOjE2NTIyMjE1MDgsImV4cCI6MTY1MjIyNTEwOH0.bWzGkzRyb51_PGuZsa7oexvP9f-8ORtdNfqNcnky8BM
' +
'User-Agent: axios/0.21.4
' +
'Content-Length: 2
' +
'Host: 192.168.1.8
' +
'Connection: keep-alive
' +
'
',
_keepAliveTimeout: 0,
_onPendingData: [Function: noopPendingOutput],
agent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 443,
protocol: 'https:',
options: [Object],
requests: {},
sockets: {},
freeSockets: [Object],
keepAliveMsecs: 1000,
keepAlive: true,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 0,
maxCachedSessions: 100,
_sessionCache: [Object],
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: 'POST',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/proxy/network/dl/firmware/bundles.json',
_ended: true,
res: IncomingMessage {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
socket: null,
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers: [Object],
rawHeaders: [Array],
trailers: {},
rawTrailers: [],
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 403,
statusMessage: 'Forbidden',
client: [TLSSocket],
_consuming: false,
_dumped: false,
req: [Circular *1],
[Symbol(kCapture)]: false,
[Symbol(RequestTimeout)]: undefined
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: true,
host: '192.168.1.8',
protocol: 'https:',
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [Array],
'x-csrf-token': [Array],
'content-type': [Array],
cookie: [Array],
'user-agent': [Array],
'content-length': [Array],
host: [Array]
}
},
response: {
status: 403,
statusText: 'Forbidden',
headers: {
vary: 'Origin',
'x-dns-prefetch-control': 'off',
'x-frame-options': 'SAMEORIGIN',
'strict-transport-security': 'max-age=15552000; includeSubDomains',
'x-download-options': 'noopen',
'x-content-type-options': 'nosniff',
'x-xss-protection': '1; mode=block',
'accept-ranges': 'bytes',
'content-type': 'application/json; charset=utf-8',
'x-response-time': '2ms',
'content-length': '32',
date: 'Tue, 10 May 2022 22:25:09 GMT',
connection: 'keep-alive'
},
config: {
url: 'https://192.168.1.8/proxy/network/dl/firmware/bundles.json',
method: 'post',
data: '{}',
headers: [Object],
transformRequest: [Array],
transformResponse: [Array],
timeout: 0,
withCredentials: true,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
httpsAgent: [Agent],
transitional: [Object],
jar: [CookieJar],
maxRedirects: 0,
validateStatus: [Function: validateStatus]
},
request:  ClientRequest {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: true,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: true,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 2,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [TLSSocket],
_header: 'POST /proxy/network/dl/firmware/bundles.json HTTP/1.1
' +
'Accept: application/json, text/plain, */*
' +
'X-CSRF-Token: aaaaaaa-9306-4bdf-9b45-151134d8c7df
' +
'Content-Type: application/json
' +
'Cookie: TOKEN=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.eyJ1c2VySWQiOiIwNWE5YWJiMi1hYmE1LTQ1YjMtYTc2MS0wOWJlYjQ2YWU2NDEiLCJjc3JmVG9rZW4iOiIwYjdhZDU2ZS1hYmU5LTQ3N2MtYjRjNS0wNDllZTU4MzQ4ZTUiLCJqdGkiOiIwZmFkN2E5Ni1hYmNkLTQ3YjAtODZmNy1iYjEzMWM5MjYxODYiLCJwYXNzd29yZFJldmlzaW9uIjoxNjEyMzEwNjQ2LCJpYXQiOjE2NTIyMjE1MDgsImV4cCI6MTY1MjIyNTEwOH0.bWzGkzRyb51_PGuZsa7oexvP9f-8ORtdNfqNcnky8BM
' +
'User-Agent: axios/0.21.4
' +
'Content-Length: 2
' +
'Host: 192.168.1.8
' +
'Connection: keep-alive
' +
'
',
_keepAliveTimeout: 0,
_onPendingData: [Function: noopPendingOutput],
agent: [Agent],
socketPath: undefined,
method: 'POST',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/proxy/network/dl/firmware/bundles.json',
_ended: true,
res: [IncomingMessage],
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: true,
host: '192.168.1.8',
protocol: 'https:',
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype]
},
data: { message: 'Invalid CSRF Token' }
},
isAxiosError: true,
toJSON: [Function: toJSON]
}

controller.createWLan give unsupported format in WlanConf payload.

A simple

controller.createWLan('"testssid"','"wpapskcode"','"60a52325a959c40010401efc"','"60a52325a959c40010401efd"',true,false,false,"wpapsk","auto","auto",false,null,false,false,"[]",null)
.then(result => {
console.log("done: " + result);
}).catch(error => {
console.log("error: " + error);
});

//error: Error: Request failed with status code 400

Will give this error on the Unifi controller:
2021-10-03T01:25:59,159] WARN sanitize - Unsupported format exists in WlanConf payload={name="testssid", usergroup_id="60a52325a959c40010401efc", wlangroup_id="60a52325a959c40010401efd", enabled=true, hide_ssid=false, is_guest=false, security=wpapsk, wpa_mode=auto, wpa_enc=auto, vlan_enabled=false, uapsd_enabled=false, schedule_enabled=false, schedule=null, x_passphrase="wpapskcode"}

I have tried even just to use the basic settings from the documentation, but it seems to give the same error. I am on controller Buildatag_5.14.23_13880

UDM Pro seems to need a URL without a port number

I'm running Unifi OS 3.2.7 and Unifi 8. I just switched from the USG. I found that in order to get node-unifi to work I had to modify the source code to change the URL to not have a port number. I tried ports 80, 443 and 8443 and all failed. I can't explain it -- is this a known problem

    //this._baseurl = new URL(`https://${options.host}:${options.port}`);
    this._baseurl = new URL(`https://${options.host}`);

setDeviceSettingsBase restarts UniFi Flex

I have a launchd script that runs every two hours to turn POE on and off. This script runs on my UniFi USW-Pro-24-PoE without it power cycling the entire switch; however, running the same script on the USW-Flex causes it to restart the entire switch. Is this unavoidable, or is there a different command I could use when interacting with the USW-Flex via API? I am using the script provided in this library.

https://github.com/jens-maus/node-unifi/blob/master/examples/poe-switch-onoff.js

Websocket - Disconnect after every ~1 hour - need support

I am currently playing around with the websocket interface, with the long term goal of integrating this into the iobroker unifi adapter.
I noticed that the websocket interface always stops working after about an hour, i have done serveral test.
Unfortunately I do not get on now and need please support

Here you can find my test code:
https://github.com/Scrounger/ioBroker.unifi/blob/1349aa3607ef147c66b2be3cc8acebfa9dfea6bb/main.js#L1423

And here are two logs of one run, all others show the same behaviour:

unifi-ws-log-1.log
unifi-ws-log-2.log

Test setup:
UDM Pro: v3.1.16 (Unifi OS)
Network: 7.5.176

CSRF token not always set

Thanks for this package. I've been using v1.x for more than a year and it's been working very stable. But with the most recent UnifiOS and Network application update, my simple LED updater stopped working, so I went on to update node-unifi to v2.

But I hit a bit of an issue. This one actually took me a few hours to understand (though I have not yet been able to figure out a solution, only a workaround), and it seems to be related to #157.

I am doing the following:

const unifi = new Controller({ host, port, username, password, sslverify: false });
await unifi.login();
await unifi.setLEDOverride(id as string, value ? 'on' : 'off');

But I get a 403 error with "Invalid CSRF Token" because no token is set.

It seems login does not set a token, and if I do not have another request of some kind before doing the LED override, then it will fail due to the token not being set.

As a workaround I have the following line before setLEDOverride:

// This is a workaround to make sure the CRSF token is updated
await unifi.getAccessDevices(mac);

This will get data for the device, that I don't need, but it will also update the CSRF token from the response.

I guess login should take care of this or _init should but neither seem to do that. Or maybe _request can check for it and pull it if not available.

Workaround works well for now and I'm just doing one extra request, but if you have happen to get a fix done, then I am ready to test that :)

I think this could affect many other commands if used without pulling device data before.

Authorized guest 25 second after authorized with external cloud controller

Hi,

When I call authorized guest with my external controller for the guest can access to internet after 25 second.
It's work faster with local hosted controller

I tryed to send ap mac address for faster authorization but nothing change...

controller.authorizeGuest(site, mac, duration, function(err, data) {
console.log(err, data);
}, undefined, undefined, undefined, ap);
});

I don't know if I correct ? I can not find a solution
If I use unifi guest portal its very fast.

thanks

FlexHD LED colour

The FlexHD makes it possible to control the LED colour and intensity of the status ring. It would be nice if the adapter could do this. Then you could use the beautifully shaped FlexHD as a status display. Perhaps other Unifi devices also offer this possibility. This would be a nice option .

getFullStatus and getDeviceNameMappings returning undefined

Dear,

when using these both methods, it's returning undefined :

controller.login()
	  .then(result => {
		console.log('login: ' + result);
		return controller.getFullStatus();
	  })
	  .then(result => {
		console.log('getFullStatus: ' + result);
		console.log('_last_results_raw: ' + controller._last_results_raw);
		return controller.getDeviceNameMappings();
	  }) .then(result => {
		console.log('getDeviceNameMappings: ' + result);
		console.log('_last_results_raw: ' + controller._last_results_raw);
		return controller.listen();
	  })....

for getDeviceNameMappings if i comment the line "result = this._last_results_raw;" it works

Some methods require a PUT instead of POST

By looking through the code, the methods for updating settings in unifi, is hardwired to use POST method.

However, I have recently found out that unifi uses a PUT method, when updating things like user groups, (editUserGroup)

It should be configurable to be able to use the correct HTTP method to post / update / delete, regardless, even if json is supplied.

Failed login doesn't reject

I'm trying to write a script that pulls event logs. I plan to automate it. I have everything working but I want to make sure there is some error handling so if something goes wrong I can have a log of it. However, when I try to log in with wrong credentials I never get a callback. The code below prints "Good" when the credentials are OK, but doesn't print "Bad" when they are not.

const unifi = require('node-unifi');
const controller = new unifi.Controller({host: 'XXXXXX', port: 443, sslverify: false});
const fs = require('fs');

const workflow = controller.login('XXXXX', 'XXXXX')
.then(result => {
  console.log("Good");
}).catch(err => {
  console.log("Bad");
});

Port Control

I would like to modify this to allow changing port, specifically POE.
It looks like i need to login and use controller.getAccessDevices(site, function(err,data)), perhaps include the mac address for the switch I want just to make it easier then do a data[0].forEach(function(device)) and look for device.type == "usw". I can probably skill all this and just use the first device if I pass desired mac address though.

From there I take that device and get the _id and device_id and possibly the contents of port_overrides. I then use those two pieces of data to build a new request. It looks like while using the same cookie I pass a request to https://unifi:8443/api/s/default/rest/device/<_id> where the contents look something like

{"port_overrides":[{"port_idx":<port>,"portconf_id":"<device_id>","poe_mode":"<off|passv24|auto>"}]}

Now this is where I'm a little fuzzy. I think i may need to take device.port_overrides and modify the whole thing either adding or changing just the port_idx that I want to change. safest assumption is to probably modify the whole thing and pass it back, but my only device lab is my home network and i'm hesitant to risk taking out my whole network.

I'll be working up a fork and then a pr. I could use input on how to design the api if anyone has any suggestions. I was thinking maybe a helper class something like unifiAccessDevice that you feed a switch from getaccessdevices(site, cb, mac) to and it then validates that it is a switch and then exposes the poe. with a final controller.updateAccessDevice() maybe that writes the result back to the rest api. for now the helper class could verify that the passed device is a switch since we won't be supporting anything else and throw an exception if it gets invalid input.

This will leave an api that can be expanded to support more devices in the future, but for now will do what I need

Websocket - self-hostd UniFi controller software

Hi there,
I tried the example, but I don't get any data from the controller.
Anyone an idea what's wrong?

This is the Output:

--> node app.js
login: true
listen: true
ctrl.connect
ctrl.pong
ctrl.pong
ctrl.pong
ctrl.pong
ctrl.pong
ctrl.pong
ctrl.pong
ctrl.pong
ctrl.pong

The API-Example works.

THANKS.

Callback already called

Currently I use this library to log the dashboard data from unifi. The script runs every hour and stores the data in a mysql database. But sometimes I receive these strange error messages:

 Error: Callback was already called.
     at /home/stephan/nodejs/wanspeed/node_modules/async/dist/async.js:903:32
     at next (/home/stephan/nodejs/wanspeed/node_modules/async/dist/async.js:5195:25)
     at Request.<anonymous> (/home/stephan/nodejs/wanspeed/node_modules/node-unifi/unifi.js:1369:11)
     at emitOne (events.js:101:20)
     at Request.emit (events.js:191:7)
     at Request.onRequestError (/home/stephan/nodejs/wanspeed/node_modules/request/request.js:884:8)
     at emitOne (events.js:96:13)
     at ClientRequest.emit (events.js:191:7)
     at TLSSocket.socketErrorListener (_http_client.js:358:9)
     at emitOne (events.js:96:13)

This is the code I use to get the dashboard data:

controller.login("user", "password", function(err) {

	if (err) {
		console.log('ERROR: ' + err);
		return;
	}

	controller.getDashboard('default', function(err, client_data) {
		if (err) {
			console.log(err);
		}
		for (var i = 0; i < client_data[0].length; i++) {
			//database stuff...
		}

		controller.logout();

	}.bind({
		conn: conn
	}));
});

Is there a problem with my code?

Critical Notifications Query Parameter in WebSocket URL

Hi,

Maybe its more a feature request then a bug. But there is an second "channel" in Unifi to listen for Critical notifications.

Is there a way to listen to both of this websockets, or a flag/opts to add the needed query param.

/proxy/network/wss/s/default/events?clients=v2&critical_notifications=true
/proxy/network/wss/s/default/events?clients=v2

Screenshot 2024-02-23 at 10 21 14

axios error 401 with Network v8+ / UniFiOS 3.2+

I am trying to find out where my bug/error/issue is.

my tool is using node-unifi and since I upgraded to a new version of the controller 8.02 I get an error 401
I tried multiple things: username, password, creating new users but always an error 401

so to eliminate things I installed a fresh virtual server machine and installed npm and the node-unifi tool with the sample script first step.
also here
ERROR: AxiosError: Request failed with status code 401

To further see if it is my setup I used a python and php testscript which was working fine.

So it seems that after an upgrade of 7.x to 8.02 there is something different now where other modules/classes in another language still work.

I understand that 8.02 is still early access (normally I do not upgrade it but my finger was too quick pressing the mouse .. )
tried to update node-unifi but it might be an axios issue? But I do not see if somebody else is experiencing same issue.

[INFORMATION] Websockets

Hello,

on unifiOS lot of websockets events come from : wss://unifi/api/ws/system
with an object like :

interface IControllerEvent {
    type: string;
    [key: string]: unknown;
}

And it seems you just listen on : wss://unifi[/proxy/network]/wss/s/<siteName>/events .
But on unifi ( non unifiOS ), some events are send on the "super" site wss://unifi[/proxy/network]/wss/s/super/events ( + events on the selected site ) . They are like the other SiteEvents so ( from what I saw ) :

export interface ISiteEvent {
    meta: {
        rc: string;
        message: string;
        product_line?: string;
    };
    data: Array<unknown>;
}

Some events like "admin login" are send on the super site websockets .

It can be interessting to add a way to listen on them

2FA Support

Hi

Is there any documentation on how to login to the controller when 2FA is enabled for the account?

I receive an API Error "ERROR: api.err.Ubic2faTokenRequired"

Support the new Unifi Protect Software

Ubiquity released a new piece of hardware, the Cloud Key 2 and also the brand new Unifi Protect, which maybe will replace Unifi Video in the long run.

Are there plans to support Unifi Protect?

InvalidAccessToken error

Hi, i use to work with this great package .The webapi request with theauthorize-guest cmd was working but now i am facing an error . I try to send a post request with postman , i get alternatively api.err.LoginRequired and api.err.InvalidAccessToken when i add username and password .

Issue with Version 1.2.1

TypeError: _self.listRadiusAccounts is not a function at new Controller (/home/pi/nodejs/node_modules/node-unifi/unifi.js:1311:9) at module.exports (/home/pi/nodejs/presencenotifier.js:7:19)

I receive this error after upgrade to version 1.2.1

ioBroker - Warning

State value to set for "unifi.0.default.clients.e8:db:xx:xx:xx:xx.noted" has to be type "boolean" but received type "string"

using Unifi Adapter in ioBroker will lead to the warning message above.
This will spam the log.

Missing wan2 support on site statistics.

The library is missing support for architectures using wan2 connections by default on site statistics by hardcoding the response fields.

Without wan2 support:

{
      'wan-tx_bytes': 0,
      'wan-rx_bytes': 0,
      wlan_bytes: 25002829.956521742,
      num_sta: 1027,
      'lan-num_sta': 1026,
      'wlan-num_sta': 1,
      time: 1611252000000,
      site: '5ed7f1c5d5497702636beb4f',
      o: 'site',
      oid: '5ed7f1c5d5497702636beb4f'
}

With wan2 support:

{
      'wan-tx_bytes': 0,
      'wan-rx_bytes': 0,
      'wan2-tx_bytes': 770449810.1983193,
      'wan2-rx_bytes': 307687031.9610084,
      wlan_bytes: 25002829.956521742,
      num_sta: 1027,
      'lan-num_sta': 1026,
      'wlan-num_sta': 1,
      time: 1611252000000,
      site: '5ed7f1c5d5497702636beb4f',
      o: 'site',
      oid: '5ed7f1c5d5497702636beb4f'
}

Exemple for auth guest?

Hi,

Great job I'm a noob with nodejs
Could you give me an exemple for authorize guest ?
I don't know how I can call this function.
Thanks

WebSocket crash on line 2922

WebSocket interval crash on line 2922 when connection is lost.

Error: WebSocket is not open: readyState 0 (CONNECTING)
File "/node_modules/ws/lib/websocket.js", line 441, col 13, in WebSocket.send
throw new Error('WebSocket is not open: readyState 0 (CONNECTING)');
File "/node_modules/node-unifi/unifi.js", line 2922, col 16, in Timeout._onTimeout
this._ws.send('ping');
File "node:internal/timers", line 559, col 17, in listOnTimeout
File "node:internal/timers", line 502, col 7, in processTimers

How do you catch this exception?

What need to be enabled on Controller's interface to allow websocket listen

Dear,

as everything have been revamped on Unifi side concerning notifications and stuffs,

i'm not very sure anymore what settings need to be enabled on the Controller's interface to allow the websocket listen to eg client connect/disconnect... sometimes some of my users have issues to receive these events on UDM SE (latest version 7.3.83) but as they changed alot in event/alarm management lately, i'm not very sure... (eg on my system (software controller), all notification are disabled and i receive client connect/disconnect, but one of my users with all notification enabled receive nothing....)

could you clarify exactly what's needed ?

Return voucher create_time

Hi,
I've just started looking into this, I previously used the PHP version, however I can't find any documentation of your JS build.

Generating the voucher is working as expected, which is great, however the callback/return isn't being piped? Am I missing something, or doing something incorrect?

var unifi = require('node-unifi');
var controller = new unifi.Controller(config.UBQT.Address, config.UBQT.Port);

let thisclass = {
  GenerateVoucher: function()
  {
    controller.login(config.UBQT.User, config.UBQT.Password, function(err) {
      if(err) {
        console.log('ERROR: ' + err);
        return;
      }

      controller.createVouchers(config.UBQT.Site, config.UBQT.Expiry, '', "1", "1", "WiFi Voucher via Node", config.UBQT.MaxUp, config.UBQT.MaxDown, function(err, result) {
        if(err)
        {
          console.log(err);
          return;
        }
        alert('data: '+ result);
        controller.logout();
      });
    });
  }
}


module.exports = thisclass;

within this function, what is "cb"?

Any help on this would be greatly appreciated.

Thank you 👍

Getting invalid payload

When using the setwlansettings and createwlan functions i get a Err.InvalidPayload error. I am using version 5.6.22 of the unifiController. Have the api calls changed?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.