This library is used at least for the following adapters:
const TTL_SEC = 3600;
const SocketAdmin = require('@iobroker/socket-classes').SocketAdmin;
const ws = require('@iobroker/ws-server');
const session = require('express-session');
const utils = require('@iobroker/adapter-core'); // Get common adapter utils
const AdapterStore = require(utils.controllerDir + '/lib/session.js')(session, TTL_SEC);
const store = new AdapterStore({adapter});
const io = new SocketAdmin(adapter.config, adapter, objects);
io.start(
server,
ws,
{
userKey: 'connect.sid',
store,
secret: adapter.config.secret
}
);
// subscribe on all object changes
io.subscribe('objectChange', '*');
// later
io.close();const TTL_SEC = 3600;
const ws = require('@iobroker/ws-server');
const SocketWS = require('@iobroker/socket-classes').SocketCommon;
const session = require('express-session');
const utils = require('@iobroker/adapter-core'); // Get common adapter utils
const AdapterStore = require(utils.controllerDir + '/lib/session.js')(session, TTL_SEC);
const store = new AdapterStore({adapter});
const settings = adapter.config;
settings.crossDomain = true;
settings.ttl = settings.ttl || TTL_SEC;
const io = new SocketWS(settings, adapter);
io.start(server.server, ws, {userKey: 'connect.sid', checkUser, store, secret: adapter.config.secret});
// later
io.close();clientSubscribe and clientUnsubscribe deliver a message to an adapter instance and therefore
require the same permission as sendTo: other.sendto. Users without it receive a permission error.
GUI client can send to desired instance the subscribe message
socket.emit('clientSubscribe', 'cameras.0', 'startCamera', { width: 640, height: 480 }, result => console.log('Started: ' + result));The instance 'cameras.0' will receive message clientSubscribe with information who want to receive messages.
adapter.on('message', obj => {
if (obj?.command === 'clientSubscribe') {
if (obj?.message.type && obj.message.type.startsWith('startCamera/')) {
const [, camera] = obj.message.type.split('/');
// start camera with obj.message.data
// ...
// inform GUI that camera is started
adapter.sendTo(obj.from, obj.command, {result: true}, obj.callback);
this.subscribes = this.subscribes || [];
this.subscribes.push({sid: obj.message.sid, from: obj.from, type: obj.message.type, camera});
}
} else if (obj?.command === 'clientUnsubscribe' || obj?.command === 'clientSubscribeError') {
if (obj?.message.type && obj.message.type.startsWith('startCamera/')) {
const [, camera] = obj.message.type.split('/');
if (this.subscribes) {
const pos = this.subscribes.findIndex(s => s.sid === obj.message.sid && s.from === obj.from && s.type === obj.message.type);
if (pos !== -1) {
this.subscribes.splice(pos, 1);
// stop camera
// ...
}
}
}
}
});and after that the client will receive messages from instance
function sendImage(camera, data) {
this.subscribes.forEach(it => {
if (it.camera !== camera) {
return;
}
// send image to GUI
adapter.sendTo(it.from, 'im', {m: it.type, s: it.sid, d: data});
});
}authenticateupdateTokenExpirationerrorlogcheckFeatureSupportedgetHistoryhttpGetsendTosendToHostauthEnabledlogoutlistPermissionsgetUserPermissionsgetVersiongetAdapterNameclientSubscribeclientUnsubscribegetCompactSystemConfiggetAdapterInstancesgetObjectgetObjectsgetAllObjectssubscribeObjectsunsubscribeObjectsgetObjectViewsetObjectdelObjectgetStatesgetForeignStatesgetStatesetStategetBinaryStatesetBinaryStatesubscribesubscribeStatesunsubscribeunsubscribeStatesreadFilereadFile64writeFile64writeFileunlinkdeleteFiledeleteFolderrenameFilerenamemkdirreadDirchmodFilechownFilefileExistssubscribeFilesunsubscribeFiles
Wait till the user is authenticated. As the user authenticates himself, the callback will be called
callback(isUserAuthenticated: boolean, isAuthenticationUsed: boolean) => void) => void: Callback(isUserAuthenticated: boolean, isAuthenticationUsed: boolean) => void
After the access token is updated, this command must be called to update the session (Only for OAuth2)
accessTokenstring: New access tokencallback(error: string | undefined | null, success?: boolean) => void) => void: Callback(error: string | undefined | null, success?: boolean) => void
Write error into ioBroker log
errorError | string: Error object or error text
Write log entry into ioBroker log
textstring: log textlevelioBroker.LogLevel: one of['silly', 'debug', 'info', 'warn', 'error']. The default is 'debug'.
Check if the same feature is supported by the current js-controller
featureSupportedFeature: feature name likeCONTROLLER_LICENSE_MANAGERcallback(error: string | Error | null | undefined, isSupported?: boolean) => void) => void: callback(error: string | Error | null | undefined, isSupported: boolean) => void
Get the history data from the specific instance
idstring: object IDoptionsioBroker.GetHistoryOptions: History optionscallback(error: string | Error | null | undefined, result: ioBroker.GetHistoryResult) => void) => void: callback(error: string | Error | null | undefined, result: ioBroker.GetHistoryResult) => void
Read content of HTTP(s) page server-side (without CORS and stuff)
urlstring: Page URLcallback(error: Error | null | undefined | string, result?: {status: number; statusText: string}, data?: string) => void: callback(error: Error | null, result?: { status: number; statusText: string }, data?: string) => void
Send the message to specific instance
adapterInstancestring: instance name, e.g.history.0commandstring: command namemessageany: the message is instance-dependentcallback(result: any) => void) => void: callback(result: any) => void
Send a message to the specific host.
Host can answer to the following commands: cmdExec, getRepository, getInstalled, getInstalledAdapter, getVersion, getDiagData, getLocationOnDisk, getDevList, getLogs, getHostInfo, delLogs, readDirAsZip, writeDirAsZip, readObjectsAsZip, writeObjectsAsZip, checkLogging, updateMultihost.
hoststring: Host name. With or without 'system.host.' prefixcommand* 'shell' | 'cmdExec' | 'getRepository' | 'getInstalled' | 'getInstalledAdapter' | 'getVersion' | 'getDiagData' | 'getLocationOnDisk' | 'getDevList' | 'getLogs' | 'getLogFile' | 'getLogFiles' | 'getHostInfo' | 'getHostInfoShort' | 'delLogs' | 'readDirAsZip' | 'writeDirAsZip' | 'readObjectsAsZip' | 'writeObjectsAsZip' | 'checkLogging' | 'updateMultihost' | 'upgradeController' | 'upgradeAdapterWithWebserver' | 'getInterfaces' | 'upload' | 'rebuildAdapter' | 'readBaseSettings' | 'writeBaseSettings' | 'addNotification' | 'clearNotifications' | 'getNotifications' | 'updateLicenses' | 'upgradeOsPackages' | 'restartController' | 'sendToSentry'*: Host commandmessageany: the message is command-specificcallback(result: {error?: string; result?: any}) => void) => void: callback(result: { error?: string; result?: any }) => void
Ask server is authentication enabled, and if the user authenticated
callback(isUserAuthenticated: boolean | Error | string, isAuthenticationUsed: boolean) => void) => void: callback(isUserAuthenticated: boolean | Error | string, isAuthenticationUsed: boolean) => void
Logout user
callbackioBroker.ErrorCallback: callback(error?: Error) => void
List commands and permissions
callback(permissions: Record< string, {type: 'object' | 'state' | 'users' | 'other' | 'file' | ''; operation: SocketOperation} >) => void: callback(permissions: Record<string, { type: 'object' | 'state' | 'users' | 'other' | 'file' | ''; operation: SocketOperation }>) => void
Get user permissions
callback(error: string | null | undefined, userPermissions?: SocketACL | null) => void) => void: callback(error: string | null | undefined, userPermissions?: SocketACL | null) => void
Get the adapter version. Not the socket-classes version!
callback(error: string | Error | null | undefined, version: string | undefined, adapterName: string) => void: callback(error: string | Error | null | undefined, version: string | undefined, adapterName: string) => void
Get adapter name: "iobroker.ws", "iobroker.socketio", "iobroker.web", "iobroker.admin"
callback(error: string | Error | null | undefined, adapterName: string) => void) => void: callback(error: string | Error | null | undefined, version: string | undefined, adapterName: string) => void
Client subscribes to specific instance's messages. Client informs a specific instance about subscription on its messages. After subscription, the socket will receive "im" messages from the desired instance The target instance MUST acknowledge the subscription and return result
targetInstancestring: Instance name, e.g., 'cameras.0'messageTypestring: Message type, e.g., 'startRecording/cam1'dataany: Optional data object, e.g., {width: 640, height: 480}callback(error: string | null | Error | undefined, result?: {accepted: boolean; heartbeat?: number; error?: string}) => void: Callback(error: string | null, result?:{ accepted: boolean; heartbeat?: number; error?: string; }) => void
Client unsubscribes from specific instance's messages. The target instance MUST NOT acknowledge the un-subscription
targetInstancestring: Instance name, e.g., 'cameras.0'messageTypestring: Message type, e.g., 'startRecording/cam1'callback(error: string | null | Error | undefined) => void) => void: Callback(error: string | null) => void
Get the system configuration in a compact form to save bandwidth.
callback(error: string | null | Error | undefined, systemConfig?: {common: ioBroker.SystemConfigCommon; native?: {secret: string; vendor?: any}}) => void: - Callback function(error: string | null, systemConfig?: { common: any; native?: { secret: string } }) => void
Read all instances of the given adapter, or all instances of all adapters if adapterName is not defined
adapterNamestring | undefined: adapter name, e.g.history. To get all instances of all adapters, just place here "".callback(error: null | undefined | Error | string, instanceList?: ioBroker.InstanceObject[]) => void) => void: callback(error: null | undefined | Error | string, instanceList?: ioBroker.InstanceObject[]) => void
Get one object.
idstring: Object IDcallback(error: Error | undefined | string | null, obj?: ioBroker.Object) => void) => void: Callback(error: string | null, obj?: ioBroker.Object) => void
Get all objects that are relevant for the web: all states and enums with rooms. This is a non-admin version of "all objects" and will be overloaded in admin
liststring[] | null: Optional list of IDscallback(error: Error | undefined | string | null, objs?: Record<string, ioBroker.Object>) => void) => void: Callback(error: string | null, objs?: Record<string, ioBroker.Object>) => void
Get all objects that are relevant for the web: all states and enums with rooms.
callback(error: null | undefined | Error | string, result?: Record<string, ioBroker.Object>) => void) => void: - Callback function(error: string | null, objects?: Record<string, ioBroker.Object>) => void
Subscribe to object changes by pattern. The events will come as 'objectChange' events to the socket.
patternstring | string[]: Pattern likesystem.adapter.*or array of IDs like['system.adapter.admin.0.memRss', 'system.adapter.admin.0.memHeapTotal']callback(error: Error | undefined | string | null) => void) => void: Callback(error: string | null) => void
Unsubscribe from object changes by pattern.
patternstring | string[]: Pattern likesystem.adapter.*or array of IDs like['system.adapter.admin.0.memRss', 'system.adapter.admin.0.memHeapTotal']callback(error: string | null | Error | undefined) => void) => void: Callback(error: string | null) => void
Get a view of objects. Make a query to the object database.
designstring: Design name, e.g., 'system' or other designs likecustom, but it must exist object_design/custom. To 99,9% usesystem.searchstring: Search name, object type, likestate,instance,adapter,host, ...params{startkey?: string; endkey?: string; depth?: number}: Parameters for the query, e.g.,{startkey: 'system.adapter.', endkey: 'system.adapter.\u9999', depth?: number}callback(error: string | null | Error | undefined, result?: {rows: {id: string; value: ioBroker.Object & {virtual: boolean; hasChildren: number;};}[];}) => void: Callback(error: string | null, result?: { rows: Array<GetObjectViewItem> }) => void
Set an object.
idstring: Object IDobjioBroker.Object: Object to setcallback(error: string | null | Error | undefined) => void) => void: Callback(error: string | null) => void
Delete an object. Only deletion of flot and fullcalendar objects is allowed
idstring: Object ID, like 'flot.0.myChart'_optionsany: Options for deletion. Ignoredcallback(error: string | null | Error | undefined) => void) => void: Callback(error: string | null) => void
Get states by pattern of current adapter
patternstring | string[] | undefined: optional pattern, likesystem.adapter.*or array of state IDs. If the pattern is omitted, you will get ALL states of current adaptercallback(error: null | undefined | Error | string, states?: Record<string, ioBroker.State>) => void) => void: callback(error: null | undefined | Error | string, states?: Record<string, ioBroker.State>) => void
Same as getStates
patternstring | string[]: pattern likesystem.adapter.*or array of state IDscallback(error: null | undefined | Error | string, states?: Record<string, ioBroker.State>) => void) => void: callback(error: null | undefined | Error | string, states?: Record<string, ioBroker.State>) => void
Get a state by ID
idstring: State ID, e.g.system.adapter.admin.0.memRsscallback(error: null | undefined | Error | string, state?: ioBroker.State) => void) => void: Callback(error: null | undefined | Error | string, state?: ioBroker.State) => void
Set a state by ID
idstring: State ID, e.g.system.adapter.admin.0.memRssstateioBroker.SettableState: State value or object, e.g.{val: 123, ack: true}callback(error: null | undefined | Error | string, state?: ioBroker.State) => void) => void: Callback(error: null | undefined | Error | string, state?: ioBroker.State) => void
Get a binary state by ID
idstring: State ID, e.g.javascript.0.binarycallback(error: null | undefined | Error | string, base64?: string) => void) => void: Callback(error: null | undefined | Error | string, base64?: string) => void
Set a binary state by ID
idstring: State ID, e.g.javascript.0.binary_base64string: State value as base64 string. Binary states have no acknowledged flag.callback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
Subscribe to state changes by pattern. The events will come as 'stateChange' events to the socket.
patternstring | string[]: Pattern likesystem.adapter.*or array of states like['system.adapter.admin.0.memRss', 'system.adapter.admin.0.memHeapTotal']callback(error: string | null) => void) => void: Callback(error: string | null) => void
Subscribe to state changes by pattern. Same as subscribe.
The events will come as 'stateChange' events to the socket.
patternstring | string[]: Pattern likesystem.adapter.*or array of states like['system.adapter.admin.0.memRss', 'system.adapter.admin.0.memHeapTotal']callback(error: string | null) => void) => void: Callback(error: string | null) => void
Unsubscribe from state changes by pattern.
patternstring | string[]: Pattern likesystem.adapter.*or array of states like['system.adapter.admin.0.memRss', 'system.adapter.admin.0.memHeapTotal']callback(error: string | null) => void) => void: Callback(error: string | null) => void
Unsubscribe from state changes by pattern. Same as unsubscribe.
The events will come as 'stateChange' events to the socket.
patternstring | string[]: Pattern likesystem.adapter.*or array of states like['system.adapter.admin.0.memRss', 'system.adapter.admin.0.memHeapTotal']callback(error: string | null) => void) => void: Callback(error: string | null) => void
Read a file from ioBroker DB
adapterstring: instance name, e.g.vis.0fileNamestring: file name, e.g.main/vis-views.jsoncallback(error: null | undefined | Error | string, data: Buffer | string, mimeType: string) => void) => void: Callback(error: null | undefined | Error | string, data: Buffer | string, mimeType: string) => void
Read a file from ioBroker DB as base64 string
adapterstring: instance name, e.g.vis.0fileNamestring: file name, e.g.main/vis-views.jsoncallback(error: null | undefined | Error | string, base64?: string, mimeType?: string) => void) => void: Callback(error: null | undefined | Error | string, base64: string, mimeType: string) => void
Write a file into ioBroker DB as base64 string
adapterstring: instance name, e.g.vis.0fileNamestring: file name, e.g.main/vis-views.jsondata64string: file content as base64 stringoptions{mode?: number} | ((error: null | undefined | Error | string) => void): optional{mode: 0x0644}callback?(error: null | undefined | Error | string) => void: Callback(error: null | undefined | Error | string) => void
Write a file into ioBroker DB as text This function is overloaded in admin (because admin accepts only base64)
adapterstring: instance name, e.g.vis.0fileNamestring: file name, e.g.main/vis-views.jsondatastring: file content as textoptions?{mode?: number} | ((error: null | undefined | Error | string) => void): optional{mode: 0x0644}callback?(error: null | undefined | Error | string) => void: Callback(error: null | undefined | Error | string) => void
Delete a file in ioBroker DB
adapterstring: instance name, e.g.vis.0namestring: file name, e.g.main/vis-views.jsoncallback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
Delete a file in ioBroker DB (same as "unlink", but only for files)
adapterstring: instance name, e.g.vis.0namestring: file name, e.g.main/vis-views.jsoncallback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
Delete folder in ioBroker DB (same as unlink, but only for folders)
adapterstring: instance name, e.g.vis.0namestring: folder name, e.g.maincallback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
Rename a file in ioBroker DB
adapterstring: instance name, e.g.vis.0oldNamestring: current file name, e.g.main/vis-views.jsonnewNamestring: new file name, e.g.main/vis-views-new.jsoncallback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
Rename file or folder in ioBroker DB
adapterstring: instance name, e.g.vis.0oldNamestring: current file name, e.g.main/vis-views.jsonnewNamestring: new file name, e.g.main/vis-views-new.jsoncallback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
Create a folder in ioBroker DB
adapterstring: instance name, e.g.vis.0dirNamestring: desired folder name, e.g.maincallback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
Read the content of the folder in ioBroker DB
adapterstring: instance name, e.g.vis.0dirNamestring: folder name, e.g.mainoptionsobject | ((error: null | undefined | Error | string, files: ioBroker.ReadDirResult[]) => void): for future usecallback?(error: null | undefined | Error | string, files: ioBroker.ReadDirResult[]) => void: Callback(error: null | undefined | Error | string, files: Array<{file: string, isDir: boolean, stats: {size: number}, modifiedAt: number, acl: {owner: string, ownerGroup: string, permissions: number, read: boolean, write: boolean}}>) => void
Change a file mode in ioBroker DB
adapterstring: instance name, e.g.vis.0fileNamestring: file name, e.g.main/vis-views.jsonoptions{mode?: number}: options{mode: 0x644}callback?(error: string | Error | null | undefined) => void: Callback(error: string | Error | null | undefined) => void
Change file owner in ioBroker DB
adapterstring: instance name, e.g.vis.0fileNamestring: file name, e.g.main/vis-views.jsonoptions{owner:system.user.${string}; ownerGroup?:system.group.${string}}: options{owner: 'system.user.user', ownerGroup: 'system.group.administrator'}orsystem.user.user. If ownerGroup is not defined, it will be taken from an owner.callback?(error: null | undefined | Error | string) => void: Callback(error: null | undefined | Error | string) => void
Check if the file or folder exists in ioBroker DB
adapterstring: instance name, e.g.vis.0fileNamestring: file name, e.g.main/vis-views.jsoncallback(error: null | undefined | Error | string, exists?: boolean) => void) => void: Callback(error: null | undefined | Error | string, exists?: boolean) => void
Subscribe to file changes in ioBroker DB
idstring: instance name, e.g.vis.0or any object ID of typemeta.idcould have wildcards*too.patternstring | string[]: file name pattern, e.g.main/*.jsonor array of namescallback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
Unsubscribe from file changes in ioBroker DB
idstring: instance name, e.g.vis.0or any object ID of typemeta.idcould have wildcards*too.patternstring | string[]: file name pattern, e.g.main/*.jsonor array of namescallback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
authenticateupdateTokenExpirationerrorlogcheckFeatureSupportedgetHistoryhttpGetsendTosendToHostauthEnabledlogoutlistPermissionsgetUserPermissionsgetVersiongetAdapterNameclientSubscribeclientUnsubscribegetCompactSystemConfiggetAdapterInstancesgetHostByIprequireLogreadLogscmdExeceventsThresholdgetRatingsgetCurrentInstancedecryptencryptgetIsEasyModeStrictgetEasyModegetAdaptersupdateLicensesgetCompactInstancesgetCompactAdaptersgetObjectsCountgetCompactInstalledgetCompactSystemRepositoriesgetCompactRepositorygetCompactHostsdelStategetStatesgetForeignStatesgetStatesetStategetBinaryStatesetBinaryStatesubscribesubscribeStatesunsubscribeunsubscribeStatesaddUserdelUseraddGroupdelGroupchangePasswordgetObjectgetObjectsgetAllObjectssubscribeObjectsunsubscribeObjectsgetObjectViewsetObjectdelObjectextendObjectgetForeignObjectsdelObjectsreadFilereadFile64writeFile64writeFileunlinkdeleteFiledeleteFolderrenameFilerenamemkdirreadDirchmodFilechownFilefileExistssubscribeFilesunsubscribeFiles
Wait till the user is authenticated. As the user authenticates himself, the callback will be called
callback(isUserAuthenticated: boolean, isAuthenticationUsed: boolean) => void) => void: Callback(isUserAuthenticated: boolean, isAuthenticationUsed: boolean) => void
After the access token is updated, this command must be called to update the session (Only for OAuth2)
accessTokenstring: New access tokencallback(error: string | undefined | null, success?: boolean) => void) => void: Callback(error: string | undefined | null, success?: boolean) => void
Write error into ioBroker log
errorError | string: Error object or error text
Write log entry into ioBroker log
textstring: log textlevelioBroker.LogLevel: one of['silly', 'debug', 'info', 'warn', 'error']. The default is 'debug'.
Check if the same feature is supported by the current js-controller
featureSupportedFeature: feature name likeCONTROLLER_LICENSE_MANAGERcallback(error: string | Error | null | undefined, isSupported?: boolean) => void) => void: callback(error: string | Error | null | undefined, isSupported: boolean) => void
Get the history data from the specific instance
idstring: object IDoptionsioBroker.GetHistoryOptions: History optionscallback(error: string | Error | null | undefined, result: ioBroker.GetHistoryResult) => void) => void: callback(error: string | Error | null | undefined, result: ioBroker.GetHistoryResult) => void
Read content of HTTP(s) page server-side (without CORS and stuff)
urlstring: Page URLcallback(error: Error | null | undefined | string, result?: {status: number; statusText: string}, data?: string) => void: callback(error: Error | null, result?: { status: number; statusText: string }, data?: string) => void
Send the message to specific instance
adapterInstancestring: instance name, e.g.history.0commandstring: command namemessageany: the message is instance-dependentcallback(result: any) => void) => void: callback(result: any) => void
Send a message to the specific host.
Host can answer to the following commands: cmdExec, getRepository, getInstalled, getInstalledAdapter, getVersion, getDiagData, getLocationOnDisk, getDevList, getLogs, getHostInfo, delLogs, readDirAsZip, writeDirAsZip, readObjectsAsZip, writeObjectsAsZip, checkLogging, updateMultihost.
hoststring: Host name. With or without 'system.host.' prefixcommand* 'shell' | 'cmdExec' | 'getRepository' | 'getInstalled' | 'getInstalledAdapter' | 'getVersion' | 'getDiagData' | 'getLocationOnDisk' | 'getDevList' | 'getLogs' | 'getLogFile' | 'getLogFiles' | 'getHostInfo' | 'getHostInfoShort' | 'delLogs' | 'readDirAsZip' | 'writeDirAsZip' | 'readObjectsAsZip' | 'writeObjectsAsZip' | 'checkLogging' | 'updateMultihost' | 'upgradeController' | 'upgradeAdapterWithWebserver' | 'getInterfaces' | 'upload' | 'rebuildAdapter' | 'readBaseSettings' | 'writeBaseSettings' | 'addNotification' | 'clearNotifications' | 'getNotifications' | 'updateLicenses' | 'upgradeOsPackages' | 'restartController' | 'sendToSentry'*: Host commandmessageany: the message is command-specificcallback(result: {error?: string; result?: any}) => void) => void: callback(result: { error?: string; result?: any }) => void
Ask server is authentication enabled, and if the user authenticated
callback(isUserAuthenticated: boolean | Error | string, isAuthenticationUsed: boolean) => void) => void: callback(isUserAuthenticated: boolean | Error | string, isAuthenticationUsed: boolean) => void
Logout user
callbackioBroker.ErrorCallback: callback(error?: Error) => void
List commands and permissions
callback(permissions: Record< string, {type: 'object' | 'state' | 'users' | 'other' | 'file' | ''; operation: SocketOperation} >) => void: callback(permissions: Record<string, { type: 'object' | 'state' | 'users' | 'other' | 'file' | ''; operation: SocketOperation }>) => void
Get user permissions
callback(error: string | null | undefined, userPermissions?: SocketACL | null) => void) => void: callback(error: string | null | undefined, userPermissions?: SocketACL | null) => void
Get the adapter version. Not the socket-classes version!
callback(error: string | Error | null | undefined, version: string | undefined, adapterName: string) => void: callback(error: string | Error | null | undefined, version: string | undefined, adapterName: string) => void
Get adapter name: "iobroker.ws", "iobroker.socketio", "iobroker.web", "iobroker.admin"
callback(error: string | Error | null | undefined, adapterName: string) => void) => void: callback(error: string | Error | null | undefined, version: string | undefined, adapterName: string) => void
Client subscribes to specific instance's messages. Client informs a specific instance about subscription on its messages. After subscription, the socket will receive "im" messages from the desired instance The target instance MUST acknowledge the subscription and return result
targetInstancestring: Instance name, e.g., 'cameras.0'messageTypestring: Message type, e.g., 'startRecording/cam1'dataany: Optional data object, e.g., {width: 640, height: 480}callback(error: string | null | Error | undefined, result?: {accepted: boolean; heartbeat?: number; error?: string}) => void: Callback(error: string | null, result?:{ accepted: boolean; heartbeat?: number; error?: string; }) => void
Client unsubscribes from specific instance's messages. The target instance MUST NOT acknowledge the un-subscription
targetInstancestring: Instance name, e.g., 'cameras.0'messageTypestring: Message type, e.g., 'startRecording/cam1'callback(error: string | null | Error | undefined) => void) => void: Callback(error: string | null) => void
Get the system configuration in a compact form to save bandwidth.
callback(error: string | null | Error | undefined, systemConfig?: {common: ioBroker.SystemConfigCommon; native?: {secret: string; vendor?: any}}) => void: - Callback function(error: string | null, systemConfig?: { common: any; native?: { secret: string } }) => void
Read all instances of the given adapter, or all instances of all adapters if adapterName is not defined
adapterNamestring | undefined: adapter name, e.g.history. To get all instances of all adapters, just place here "".callback(error: null | undefined | Error | string, instanceList?: ioBroker.InstanceObject[]) => void) => void: callback(error: null | undefined | Error | string, instanceList?: ioBroker.InstanceObject[]) => void
Read the host object by IP address.
ipstring: - IP address, e.g.,192.168.1.1. IPv4 or IPv6callback?(error: string | null | Error | undefined, hostObject?: ioBroker.HostObject | null) => void: - Callback function(ip: string, obj: ioBroker.HostObject | null) => void
Activate or deactivate logging events. Events will be sent to the socket as log events. Adapter must have common.logTransporter = true.
isEnabledboolean: - Is logging enabledcallback?(error: string | null | Error | undefined) => void: - Callback function(error: string | null) => void
Get the log files from the given host.
hoststring: - Host ID, e.g.,system.host.raspberrypicallback?(error: string | null | Error | undefined, list?: {fileName: string; size: number}[]) => void: - Callback function(error: string | null, list?: { fileName: string; size: number }[]) => void
Execute the shell command on host/controller.
The following response commands are expected: cmdStdout, cmdStderr, cmdExit.
hoststring: - Host name, e.g.,system.host.raspberrypiidnumber: - Session ID, e.g.,Date.now(). This session ID will come in eventscmdStdout,cmdStderr,cmdExitcmdstring: - Command to executefiles?CommandFile[] | ((error: string | null | Error | undefined) => void): - Optional files to send with the command (base64 encoded). The command can refer to them just by name. Requires controller featureCONTROLLER_CMD_EXEC_FILES.callback?(error: string | null | Error | undefined) => void: - Callback function(error: string | null) => void
Enable or disable the event threshold. Used only for admin to limit the number of events to the front-end.
isActiveboolean: - If true, then events will be limited
Get the ratings of adapters.
updateboolean | ((error: string | null | Error | undefined, ratings?: Ratings) => void): - If true, the ratings will be read from the central server, if false from the local cachecallback?(error: string | null | Error | undefined, ratings?: Ratings) => void: - Callback function(error: string | null, ratings?: Ratings) => void
Get the current instance name, like "admin.0"
callback(error: string | null | Error | undefined, namespace: string) => void) => void: - Callback function(error: string | null, namespace?: string) => void
Decrypts text with the system secret key.
encryptedTextstring: - Encrypted textcallback(error: string | null | Error | undefined, decryptedText?: string) => void) => void: - Callback function(error: string | null, decryptedText?: string) => void
Encrypts text with the system secret key.
plainTextstring: - Plain text to encryptcallback(error: string | null | Error | undefined, encryptedText?: string) => void) => void: - Callback function(error: string | null, encryptedText?: string) => void
Get if the admin has easy mode enabled.
callback(error: string | null | Error | undefined, isEasyModeStrict?: boolean) => void) => void: - Callback function(error: string | null, isEasyModeStrict?: boolean) => void
Get easy mode configuration.
callback(error: string | null | Error | undefined, easyModeConfig?: {strict: boolean; configs: InstanceConfig[]}) => void: - Callback function(error: string | null, easyModeConfig?: { strict: boolean; configs: InstanceConfig[] }) => void
Get all adapter as objects.
adapterNamestring: - Optional adapter namecallback(error: string | null | Error | undefined, result?: ioBroker.AdapterObject[]) => void) => void: - Callback function(error: string | null, results?: ioBroker.Object[]) => void
Read software licenses (vis, knx, ...) from ioBroker.net cloud for given user
loginstring: - Cloud loginpasswordstring: - Cloud passwordcallback(error: string | null | Error | undefined, result?: License[]) => void) => void: - Callback function(error: string | null, results?: License[]) => void
Get all instances in a compact form to save bandwidth.
callback(error: string | null | Error | undefined, result?: Record<string, CompactInstanceInfo>) => void) => void: - Callback function(error: string | null, results?: Record<string, { adminTab: boolean; name: string; icon: string; enabled: boolean }>) => void
Get all adapters in a compact form to save bandwidth.
callback(error: string | null | Error | undefined, result?: Record<string, CompactAdapterInfo>) => void) => void: - Callback function(error: string | null, results?: Record<string, { icon: string; v: string; iv: string }>) => void
Count the objects and the objects of every type.
Reading all objects only to count them transfers the whole database - tens of megabytes on
a grown installation, and it blocks this process while it packs them up. This counts them
where they are and answers with numbers. Announced as the feature OBJECTS_COUNT.
callback(error: string | null | Error | undefined, result?: ObjectsCount) => void) => void: - Callback function(error: string | null, result?: ObjectsCount) => void
Get all installed adapters in a compact form to save bandwidth.
hoststring: - Host name, e.g.,system.host.raspberrypicallback(result?: Record<string, {version: string}>) => void) => void: - Callback function(error: string | null, results?: Record<string, { version: string }>) => void
Get system repositories in a compact form to save bandwidth.
callback(error: string | null | Error | undefined, systemRepositories?: CompactSystemRepository) => void) => void: - Callback function(error: string | null, systemRepositories?: { common: any; native?: { repositories: Record<string, { json: { _repoInfo: any } } } }) => void
Get the repository in a compact form to save bandwidth. Answered from the objects admin holds in memory where possible. Asking the host means several megabytes of repository through the message box for two fields per adapter, which made every start of the GUI wait seconds for it.
hoststring: - Host name, e.g.,system.host.raspberrypicallback(result: Record<string, {version: string; icon?: string}>) => void) => void: - Callback function(error: string | null, results?: Record<string, { version: string; icon?: string }>) => void
Get all hosts in a compact form to save bandwidth.
callback(error: string | null | Error | undefined, hosts?: CompactHost[]) => void) => void: - Callback function(error: string | null, results?: Record<string, { common: { name: string; icon: string; color: string; installedVersion: string }; native: { hardware: { networkInterfaces: any[] } } }>) => void
Delete a state. The corresponding object will be deleted too.
idstring: - State IDcallback?(error: string | null | Error | undefined) => void: - Callback function(error: string | null) => void
Get states by pattern of current adapter
patternstring | string[] | undefined: optional pattern, likesystem.adapter.*or array of state IDs. If the pattern is omitted, you will get ALL states of current adaptercallback(error: null | undefined | Error | string, states?: Record<string, ioBroker.State>) => void) => void: callback(error: null | undefined | Error | string, states?: Record<string, ioBroker.State>) => void
Same as getStates
patternstring | string[]: pattern likesystem.adapter.*or array of state IDscallback(error: null | undefined | Error | string, states?: Record<string, ioBroker.State>) => void) => void: callback(error: null | undefined | Error | string, states?: Record<string, ioBroker.State>) => void
Get a state by ID
idstring: State ID, e.g.system.adapter.admin.0.memRsscallback(error: null | undefined | Error | string, state?: ioBroker.State) => void) => void: Callback(error: null | undefined | Error | string, state?: ioBroker.State) => void
Set a state by ID
idstring: State ID, e.g.system.adapter.admin.0.memRssstateioBroker.SettableState: State value or object, e.g.{val: 123, ack: true}callback(error: null | undefined | Error | string, state?: ioBroker.State) => void) => void: Callback(error: null | undefined | Error | string, state?: ioBroker.State) => void
Get a binary state by ID
idstring: State ID, e.g.javascript.0.binarycallback(error: null | undefined | Error | string, base64?: string) => void) => void: Callback(error: null | undefined | Error | string, base64?: string) => void
Set a binary state by ID
idstring: State ID, e.g.javascript.0.binary_base64string: State value as base64 string. Binary states have no acknowledged flag.callback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
Subscribe to state changes by pattern. The events will come as 'stateChange' events to the socket.
patternstring | string[]: Pattern likesystem.adapter.*or array of states like['system.adapter.admin.0.memRss', 'system.adapter.admin.0.memHeapTotal']callback(error: string | null) => void) => void: Callback(error: string | null) => void
Subscribe to state changes by pattern. Same as subscribe.
The events will come as 'stateChange' events to the socket.
patternstring | string[]: Pattern likesystem.adapter.*or array of states like['system.adapter.admin.0.memRss', 'system.adapter.admin.0.memHeapTotal']callback(error: string | null) => void) => void: Callback(error: string | null) => void
Unsubscribe from state changes by pattern.
patternstring | string[]: Pattern likesystem.adapter.*or array of states like['system.adapter.admin.0.memRss', 'system.adapter.admin.0.memHeapTotal']callback(error: string | null) => void) => void: Callback(error: string | null) => void
Unsubscribe from state changes by pattern. Same as unsubscribe.
The events will come as 'stateChange' events to the socket.
patternstring | string[]: Pattern likesystem.adapter.*or array of states like['system.adapter.admin.0.memRss', 'system.adapter.admin.0.memHeapTotal']callback(error: string | null) => void) => void: Callback(error: string | null) => void
Add a new user.
userstring: - User name, e.g.,benjaminpassstring: - User passwordcallback?(error: string | null | Error | undefined) => void: - Callback function(error: string | null) => void
Delete an existing user. Admin cannot be deleted.
userstring: - User name, e.g.,benjamincallback?(error: string | null | Error | undefined) => void: - Callback function(error: string | null) => void
Add a new group.
groupstring: - Group name, e.g.,usersdescioBroker.StringOrTranslated | null: - Optional descriptionaclOmit<ioBroker.PermissionSet, 'user' | 'groups'> | null: - Optional access control list object, e.g.,{"object":{"list":true,"read":true,"write":false,"delete":false},"state":{"list":true,"read":true,"write":true,"create":true,"delete":false},"users":{"list":true,"read":true,"write":false,"create":false,"delete":false},"other":{"execute":false,"http":true,"sendto":false},"file":{"list":true,"read":true,"write":false,"create":false,"delete":false}}callback?(error: string | null | Error | undefined) => void: - Callback function(error: string | null) => void
Delete an existing group. Administrator group cannot be deleted.
groupstring: - Group name, e.g.,userscallback?(error: string | null | Error | undefined) => void: - Callback function(error: string | null) => void
Change user password.
userstring: - User name, e.g.,benjaminpassstring: - New passwordcallback?(error: string | null | Error | undefined) => void: - Callback function(error: string | null) => void
Get one object.
idstring: Object IDcallback(error: Error | undefined | string | null, obj?: ioBroker.Object) => void) => void: Callback(error: string | null, obj?: ioBroker.Object) => void
Get all objects that are relevant for the web: all states and enums with rooms. This is a non-admin version of "all objects" and will be overloaded in admin
liststring[] | null: Optional list of IDscallback(error: Error | undefined | string | null, objs?: Record<string, ioBroker.Object>) => void) => void: Callback(error: string | null, objs?: Record<string, ioBroker.Object>) => void
Get all objects that are relevant for the web: all states and enums with rooms.
callback(error: null | undefined | Error | string, result?: Record<string, ioBroker.Object>) => void) => void: - Callback function(error: string | null, objects?: Record<string, ioBroker.Object>) => void
Subscribe to object changes by pattern. The events will come as 'objectChange' events to the socket.
patternstring | string[]: Pattern likesystem.adapter.*or array of IDs like['system.adapter.admin.0.memRss', 'system.adapter.admin.0.memHeapTotal']callback(error: Error | undefined | string | null) => void) => void: Callback(error: string | null) => void
Unsubscribe from object changes by pattern.
patternstring | string[]: Pattern likesystem.adapter.*or array of IDs like['system.adapter.admin.0.memRss', 'system.adapter.admin.0.memHeapTotal']callback(error: string | null | Error | undefined) => void) => void: Callback(error: string | null) => void
Get a view of objects. Make a query to the object database.
designstring: Design name, e.g., 'system' or other designs likecustom, but it must exist object_design/custom. To 99,9% usesystem.searchstring: Search name, object type, likestate,instance,adapter,host, ...params{startkey?: string; endkey?: string; depth?: number}: Parameters for the query, e.g.,{startkey: 'system.adapter.', endkey: 'system.adapter.\u9999', depth?: number}callback(error: string | null | Error | undefined, result?: {rows: {id: string; value: ioBroker.Object & {virtual: boolean; hasChildren: number;};}[];}) => void: Callback(error: string | null, result?: { rows: Array<GetObjectViewItem> }) => void
Set an object.
idstring: Object IDobjioBroker.Object: Object to setcallback(error: string | null | Error | undefined) => void) => void: Callback(error: string | null) => void
Delete an object. Only deletion of flot and fullcalendar objects is allowed
idstring: Object ID, like 'flot.0.myChart'_optionsany: Options for deletion. Ignoredcallback(error: string | null | Error | undefined) => void) => void: Callback(error: string | null) => void
Extend the existing object.
idstring: - Object IDobjPartial<ioBroker.Object>: - New parts of the object, e.g.,{common: {name: 'new name'}}callback?(error: string | null | Error | undefined) => void: - Callback function(error: string | null) => void
Read objects by pattern.
patternstring: - Pattern likesystem.adapter.admin.0.*type* ioBroker.ObjectType | undefined | ((error: string | null | Error | undefined, objects?: Record<string, ioBroker.Object>) => void)*: - Type of objects to delete, likestate,channel,device,host,adapter. Default -statecallback?(error: string | null | Error | undefined, objects?: Record<string, ioBroker.Object>) => void: - Callback function(error: string | null, objects?: Record<string, ioBroker.Object>) => void
Delete an object or objects recursively.
Objects with dontDelete cannot be deleted.
Same as delObject but with recursive: true.
idstring: - Object ID, like 'adapterName.0.channel'options?ioBroker.DelObjectOptions | ((error: string | null | Error | undefined) => void) | null: - Options for deletion.callback?(error: string | null | Error | undefined) => void: - Callback function(error: string | null) => void
Read a file from ioBroker DB
adapterstring: instance name, e.g.vis.0fileNamestring: file name, e.g.main/vis-views.jsoncallback(error: null | undefined | Error | string, data: Buffer | string, mimeType: string) => void) => void: Callback(error: null | undefined | Error | string, data: Buffer | string, mimeType: string) => void
Read a file from ioBroker DB as base64 string
adapterstring: instance name, e.g.vis.0fileNamestring: file name, e.g.main/vis-views.jsoncallback(error: null | undefined | Error | string, base64?: string, mimeType?: string) => void) => void: Callback(error: null | undefined | Error | string, base64: string, mimeType: string) => void
Write a file into ioBroker DB as base64 string
adapterstring: instance name, e.g.vis.0fileNamestring: file name, e.g.main/vis-views.jsondata64string: file content as base64 stringoptions{mode?: number} | ((error: null | undefined | Error | string) => void): optional{mode: 0x0644}callback?(error: null | undefined | Error | string) => void: Callback(error: null | undefined | Error | string) => void
Write a file into ioBroker DB as text This function is overloaded in admin (because admin accepts only base64)
adapterstring: instance name, e.g.vis.0fileNamestring: file name, e.g.main/vis-views.jsondatastring: file content as textoptions?{mode?: number} | ((error: null | undefined | Error | string) => void): optional{mode: 0x0644}callback?(error: null | undefined | Error | string) => void: Callback(error: null | undefined | Error | string) => void
Delete a file in ioBroker DB
adapterstring: instance name, e.g.vis.0namestring: file name, e.g.main/vis-views.jsoncallback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
Delete a file in ioBroker DB (same as "unlink", but only for files)
adapterstring: instance name, e.g.vis.0namestring: file name, e.g.main/vis-views.jsoncallback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
Delete folder in ioBroker DB (same as unlink, but only for folders)
adapterstring: instance name, e.g.vis.0namestring: folder name, e.g.maincallback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
Rename a file in ioBroker DB
adapterstring: instance name, e.g.vis.0oldNamestring: current file name, e.g.main/vis-views.jsonnewNamestring: new file name, e.g.main/vis-views-new.jsoncallback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
Rename file or folder in ioBroker DB
adapterstring: instance name, e.g.vis.0oldNamestring: current file name, e.g.main/vis-views.jsonnewNamestring: new file name, e.g.main/vis-views-new.jsoncallback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
Create a folder in ioBroker DB
adapterstring: instance name, e.g.vis.0dirNamestring: desired folder name, e.g.maincallback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
Read the content of the folder in ioBroker DB
adapterstring: instance name, e.g.vis.0dirNamestring: folder name, e.g.mainoptionsobject | ((error: null | undefined | Error | string, files: ioBroker.ReadDirResult[]) => void): for future usecallback?(error: null | undefined | Error | string, files: ioBroker.ReadDirResult[]) => void: Callback(error: null | undefined | Error | string, files: Array<{file: string, isDir: boolean, stats: {size: number}, modifiedAt: number, acl: {owner: string, ownerGroup: string, permissions: number, read: boolean, write: boolean}}>) => void
Change a file mode in ioBroker DB
adapterstring: instance name, e.g.vis.0fileNamestring: file name, e.g.main/vis-views.jsonoptions{mode?: number}: options{mode: 0x644}callback?(error: string | Error | null | undefined) => void: Callback(error: string | Error | null | undefined) => void
Change file owner in ioBroker DB
adapterstring: instance name, e.g.vis.0fileNamestring: file name, e.g.main/vis-views.jsonoptions{owner:system.user.${string}; ownerGroup?:system.group.${string}}: options{owner: 'system.user.user', ownerGroup: 'system.group.administrator'}orsystem.user.user. If ownerGroup is not defined, it will be taken from an owner.callback?(error: null | undefined | Error | string) => void: Callback(error: null | undefined | Error | string) => void
Check if the file or folder exists in ioBroker DB
adapterstring: instance name, e.g.vis.0fileNamestring: file name, e.g.main/vis-views.jsoncallback(error: null | undefined | Error | string, exists?: boolean) => void) => void: Callback(error: null | undefined | Error | string, exists?: boolean) => void
Subscribe to file changes in ioBroker DB
idstring: instance name, e.g.vis.0or any object ID of typemeta.idcould have wildcards*too.patternstring | string[]: file name pattern, e.g.main/*.jsonor array of namescallback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
Unsubscribe from file changes in ioBroker DB
idstring: instance name, e.g.vis.0or any object ID of typemeta.idcould have wildcards*too.patternstring | string[]: file name pattern, e.g.main/*.jsonor array of namescallback(error: null | undefined | Error | string) => void) => void: Callback(error: null | undefined | Error | string) => void
- (@GermanBluefox) Added: an event only reaches a connection whose user may read what it is about. A subscription says which ids a client is interested in, never which ids it may see -
subscribeasks whether the user may read states at all, so whoever subscribed to*was told about every state of the system, the ACL of the single objects notwithstanding, and the same went for objects and for files. The fan-out asks that question now, for state, object and file events alike. Members of the administrator group are not restricted, as everywhere else, and with authentication switched off every connection is the configured default user - so for most installations nothing changes at all. Where restricted users exist, they will see less than before: that is the point, but it is the kind of change that comes back as "my user stopped getting updates". The database decides, not this package:mayReadof js-controller 8.0 and newer is asked where it exists, which knows the ACL of a state rather than that of its object and the mode of a single file rather than that of the adapter; an older controller is asked for the object instead, which is coarser but refuses in the same direction. One question per user and thing, remembered until the object - or the rights themselves - change, and the objects are watched from the first decision on, so an installation whose connections are all administrators never asks anything - (@GermanBluefox) Changed:
publishandpublishFileanswertruewhere the event is on its way to the client, which now includes the moment it waits for the decision described above;falsemeans the client is not subscribed to it, or may not see it. Only the first event of a thing ever waits, and only the newest one of it, so nothing can overtake it
- (@GermanBluefox) Added: every command that sends a message now names the user of the connection -
sendTo,sendToHost,cmdExec,clientSubscribe,clientUnsubscribeand the "nobody subscribed" notice. Objects, states and files have always been read and written with{ user }so the database applies the ACLs of the logged-in user; a message had no such channel, so the receiving instance sawfromand nothing else and had to act with its own rights - a GUI user who may usesendTocould have an adapter do things their own ACL forbids. It travels as a send option, which js-controller 7.2.5 and newer pass on to the message asobj.user; an older one ignores it, so nothing breaks, and a receiving adapter treats the field as optional
- (@GermanBluefox) Fixed: a websocket whose access token was not accepted stayed open without a single command handler. The client was asked to re-authenticate, fetched a new token within milliseconds and announced it with
updateTokenExpiration- the one command that is exempt from the session check exactly for this - but nobody was listening. The client waited for an answer that could not come until its own three second timeout closed the connection, burnt its single-use refresh token for nothing and had to start over, which is why the GUI took seconds to come up with authentication enabled and sometimes did not come up at all. The handlers are installed now, with an empty ACL: every command that needs a permission is still refused, only the announcement of a token gets through - (@GermanBluefox) Fixed: an announced access token now finishes the authentication of such a socket instead of only moving its expiration date. The connection carries on with the user of the token, so no reconnect and no second refresh is needed
- (@GermanBluefox) Fixed:
authenticateof a socket that is waiting for a token is answered when the token arrives, instead of being answered with "authenticated" although the socket has no user - (@GermanBluefox) Fixed:
reauthenticatewas sent twice for the same connection, so a client started two token refreshes and used up two refresh tokens - (@GermanBluefox) Changed: that a socket is not authenticated is logged at
debuginstead ofsilly, so the case is visible without raising the level of the whole adapter
- (@GermanBluefox)
getCompactRepositoryis answered from the objects admin holds in memory instead of asking the host. The host answersgetRepositorywith the whole merged repository - several megabytes through the message box for the two fields per adapter that the GUI uses - and every start of the admin GUI waited seconds for it (measured on a fast machine with three active repositories: 3140 ms, now 38 ms, with a byte-identical answer). A host without an object cache, or an active repository that was never downloaded, still goes through the host - (@GermanBluefox) The host is still asked for the repository, but ten seconds later and at most once an hour, and nobody waits for the answer: the statistics, the check for a new Docker image and for OS updates, the blocklist and the automatic adapter upgrade hang on that command and nothing else triggers them
- (@GermanBluefox) A controller that announces
CONTROLLER_REPOSITORY_COMPACTis askedgetRepositoryCompact, so the message box no longer carries the whole repository when the host has to be asked after all
- (@GermanBluefox) Added
#countObjects()method to count the objects and the objects of every type. Reading all objects only to count them transfers the whole database - tens of megabytes on a grown installation, and it blocks this process while it packs them up. This counts them where they are and answers with numbers. Announced as the featureOBJECTS_COUNT.
- (@GermanBluefox) Security: the files inside a folder are deleted and renamed with the user of the socket, so js-controller checks the permissions for every file and not only for the folder
- (@GermanBluefox)
logoutdoes not crash on a ws socket that has no_query(cookie authentication or legacy session) - (@GermanBluefox) The
objectChangeevent of a deletedsystem.configdoes not crashpublish() - (@GermanBluefox) A socket with subscriptions can be re-subscribed on a fresh commands instance without a
TypeError - (@GermanBluefox)
writeFile,delObjectanddelObjectsanswer a permission error via the callback also when the options are omitted - (@GermanBluefox) Wildcard entries of the IP white list like
192.168.1.*are matched - (@GermanBluefox) The event threshold is not disabled by a check that runs while it is being activated
- (@GermanBluefox) Instances are informed about a disconnected socket even if the socket has never subscribed to states, objects, files or logs
- (@GermanBluefox)
getAdapters(adapterName)delivers the requested adapter instead of an empty list - (@GermanBluefox)
updateRatings()sends the uuid of the system when no uuid is given - (@GermanBluefox)
getObjectViewwithdepthreturns the root object also for a start key without a trailing dot - (@GermanBluefox)
readLogsrecognizes absolute Windows paths likeC:\iobroker\log - (@GermanBluefox) Added detailed unit tests for all classes
- (@GermanBluefox) Migrated
tasks.jsto TypeScript (tasks.ts)
- (@GermanBluefox) A socket keeps working for one minute after its access token has expired and is asked to refresh the token (
reauthenticate) instead of being cut off at once. The refresh timer of a browser tab in the background fires late, so the connection was lost although the user had a valid refresh token - (@GermanBluefox)
updateTokenExpirationis accepted for a socket with an expired session, as it is the only way to make the session valid again. The new token must belong to the same user as the socket - (@GermanBluefox) Removed the dead re-read of the access token before its expiration (the condition was inverted and a token is never prolonged in place)
- (@GermanBluefox) Log messages are only sent to the sockets that subscribed to them.
sendLog()emittedlogto every connected socket, so as soon as one client calledrequireLog(true), all other clients received the log too - including clients of other browsers and clients whose user does not have the rights to subscribe to the log at all. - (@GermanBluefox)
logoutdestroys the express session again. It usedsocket.idas the session id, which only worked as long as@iobroker/ws-serverfilled the socket id with the session id from theconnect.sidcookie. The socket id is a per-connection transport identifier generated on the server now, so the session to destroy is taken fromsocket._sessionID/socket.conn.request.sessionIDinstead. - (@joltcoke) Decode the websocket auth query so encoded credentials work
- (@joltcoke) Security: an upgrade request that carries neither credentials nor a cookie header no longer crashes the adapter.
sessionIDis assigned only inside the cookie branch ofauthorize(), so such a request reached the session store lookup with it still undefined and js-controller threw while validating the id. The exception escaped the synchronousverifyClient()callback of the websocket server, so any unauthenticated client could stop an instance running withauth: true. The request is now rejected throughauth.fail(), andPassportHttpRequest.sessionIDis declared optional so the compiler rejects a future unguarded use. Affects 2.2.21 up to and including 2.3.8. Reported and fixed by Florian Schirmer. - (@GermanBluefox) Security:
authorize()answers every upgrade request exactly once now and turns an unexpected exception into a regular rejection instead of letting it escape into the upgrade handler, so a future error on that path cannot take the adapter down again.
- (@GermanBluefox) Security:
decryptandencryptonly serve a request from the process-wide cached system secret if the caller is an administrator. The cache is shared across all sockets, so once any user who may readsystem.confighad populated it, every authenticated user could encrypt and decrypt with the system secret regardless of their own rights. For non-administrators the secret is now resolved through a read that is ACL-checked for the calling user. - (@GermanBluefox) Security:
clientSubscribeandclientUnsubscribenow require theother.sendtopermission. They deliver a message to an arbitrary adapter instance viasendTo, but were not covered by any permission check, so an authenticated user withoutother.sendtocould reach any instance. Reported by Santosh Kumar Puppala. - (@GermanBluefox) Security:
eventsThresholdnow requires theother.executepermission. Enabling the threshold unsubscribes the adapter from all state patterns for every connected client, so it must not be reachable by a low-privilege user.
- (@GermanBluefox) Updated packages
- (@GermanBluefox) Corrected type
- (@GermanBluefox) Extended cmdExec with files
- (@GermanBluefox) Migrated to TS 6
- (@GermanBluefox) Implement
getAllObjectsin common commands. Made it available in web and admin. But they are different in admin and web
- (@GermanBluefox) Moved
getCompactSystemConfigto common commands. Made it available in web and admin. - (@Marc-Berg) Emit tokenInfo event with exp on socket connection
- (@Copilot) Added a missing return statement for Bearer token auth
- (@GermanBluefox) Fixed change of the language in the admin
- (@GermanBluefox) Added an option to disable filling of
info.connected
- (@GermanBluefox) Send
reauthenticatecommand if token expired
- (@GermanBluefox) Typing improvement
- (@GermanBluefox) Make secret optional for cloud usage
- (@GermanBluefox) Make objects optional for
SocketAdmin
- (@GermanBluefox) Removed debug text
- (@GermanBluefox) Deliver vendor information in
getCompactSystemConfigcommand
- (@GermanBluefox) Changed the order of authentications. Basic authentication will be checked as the last one.
- (@GermanBluefox) Added the setting to disable basic authentication
- (@GermanBluefox) Corrected functionality as a client
- (@GermanBluefox) Allowed the authentication by token in the query
- (@GermanBluefox) Removed debug text
- (@GermanBluefox) Moved to TypeScript 5.8
- (@GermanBluefox) Corrected the user's right check
- (@GermanBluefox) Added logout with bearer token
- (@GermanBluefox) Added login with a token in the query or as bearer token
- (@GermanBluefox) Added support for OAuth2 authentication
- (@GermanBluefox) Corrected language settings
- (@GermanBluefox) Code migrated to TypeScript
- (@GermanBluefox) Caught the error if no authentication and logout called
- (@GermanBluefox) Added support for iobroker.SocketIO with TypeScript
- (@GermanBluefox) Corrected call of getObjectView with null parameter
- (@GermanBluefox) updated packages
- (@GermanBluefox) extend
getCompactInstancesmethod with version information
- (foxriver76) ensure compatible
adapter-coreversion
- (@GermanBluefox) Extended getObjects function with the possibility to read the list of IDs in admin
- (@GermanBluefox) Added
publishInstanceMessageAllcommand
- (@GermanBluefox) Caught errors by subscribe/unsubscribe
- (foxriver76) do not await the subscribes anymore
- (@GermanBluefox) Corrected error by unsubscribing on client disconnect
- (foxriver76) do not cancel follow subscribes if one subscribe has an error
- (foxriver76) fixed crash on invalid patterns with js-controller version 5
- (@GermanBluefox) Implemented subscribing of a client on messages from a specific instance
- (@GermanBluefox) Moved checkFeatureSupported to regular connection and not only admin
- (foxriver76) fixed crash on invalid patterns with js-controller version 5
- (@GermanBluefox) extended the getObjects function with the possibility to read the list of IDs
- (@GermanBluefox) Added command
name
- (@GermanBluefox) Treat
json5asjson
- (@GermanBluefox) Allow deletion of fullcalendar objects
- (@GermanBluefox) Corrected error with subscription
- (@GermanBluefox) Added user check to many commands
- (@GermanBluefox) Downgrade axios to 0.27.2
- (@GermanBluefox) Function
getObjectsfor web was extended by devices, channels and enums
- (@GermanBluefox) Fixed error with delObject
- (Apollon77) Prepare for future js-controller versions
- (@GermanBluefox) Fixed error in
delObjectsmethod
- (@GermanBluefox) Caught error by subscribing
- (@GermanBluefox) Added command
getCompactSystemRepositories
- (@GermanBluefox) Buffer conversion errors caught and handled
- (@GermanBluefox) Corrected getAdapterInstances method
- (@GermanBluefox) Corrected log transportation
- (@GermanBluefox) Corrected getAdapterInstances
- (@GermanBluefox) Do not show an error with failed authentication
- (@GermanBluefox) Allowed overloading system language
- (@GermanBluefox) updated
passport
- (@GermanBluefox) allowed running socket.io behind reverse proxy
- (@GermanBluefox) Do not show the requireLog message
- (@GermanBluefox) Allowed call of getAdapterInstances for non admin
- (@GermanBluefox) Corrected renameFile command for admin
- (@GermanBluefox) Corrected changePassword command for admin
- (@GermanBluefox) Added support of socket.io 4.x
- (@GermanBluefox) Hide warn messages
- (@GermanBluefox) Added back compatibility with js-controller@4.0 for
writeDirAsZip
- (@GermanBluefox) Process
writeDirAsZiplocally
- (@GermanBluefox) fixed
getObjectscommand
- (@GermanBluefox) fixed
delObjectscommand
- (@GermanBluefox) Added support for fileChanges
- (@GermanBluefox) Corrected readLogs command and implement file subscriptions
- (@GermanBluefox) Caught some sentry errors
- (@GermanBluefox) fixed
delObjectcommand
- (@GermanBluefox) added updateRatings
- (@GermanBluefox) added passportSocket
- (@GermanBluefox) initial commit
The MIT License (MIT)
Copyright (c) 2020-2026 @GermanBluefox dogafox@gmail.com
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.