
Research
Security News
Malicious PyPI Package Exploits Deezer API for Coordinated Music Piracy
Socket researchers uncovered a malicious PyPI package exploiting Deezer’s API to enable coordinated music piracy through API abuse and C2 server control.
wappsto-wapp
Advanced tools
[](https://github.com/Wappsto/wappsto-wapp/actions/workflows/main.yml) [;
or
import Wappsto from 'wappsto-wapp';
To use it in a webpage, include this script tag:
<script src="https://cdn.jsdelivr.net/npm/wappsto-wapp@latest/dist/wappsto-wapp.min.js"></script>
Here are some examples on how to use the library.
First you need to create a IoT network. To create a new network you need to call 'createNetwork'. If there is an Network with the same name, the existing network will be returned.
let network = await Wappsto.createNetwork({
name: 'new network name',
});
Then you need to create a device. To create a new device under an existing network, you should call createDevice. If a device exists with the given name, the existing device will be returned.
let device = await network.createDevice({
name: 'Device Name',
product: 'Great Product',
serial: 'SG123456',
description: 'My great new device',
protocol: 'WAPP-JS',
communication: 'wapp',
version: '1.0.0',
manufacturer: 'My Self',
});
Then you need to create new values.
To create a new value under an existing device, you should call
createValue. If a value exists with the given name, the existing
value will be returned.
There will also be created the states needed based on the
permission. The only allowed values for permission is 'r', 'w' and
'rw'. The state will have 'NA' as the initial data. This can be changed by
setting the initialState
when creating the value. You can also define the initial
timestamp by setting initialState as an object with data and timestamp.
The list of available value templates can be seen in the
value_template.ts file.
let value = await device.createValue({
name: 'Temperature',
permission: 'r',
template: Wappsto.ValueTemplate.TEMPERATURE_CELSIUS
});
It is also possible to define a period for how often the library should poll your values for updates. In this way regularly reports will be send to the cloud. It is also possible to filter out small changes by setting the delta for number values. With the delta set, reported changes that are not bigger then the delta will be discarded. In the example below, period is set to 3600 [sec.], i.e. 1 hour interval and delta to 2 [deg.]. So changes in value data will only apply if after one hour new temperature value is bigger than 2 [deg.] of previous temperature value.
let value = await device.createValue({
name: 'Temperature',
permission: 'r',
template: Wappsto.ValueTemplate.TEMPERATURE_CELSIUS,
period: 3600,
delta: '2'
});
It is also possible to define a value where the data is not put into
the historical log. This is done by setting the disableLog
to
true
. This can be set on the createValue
and the special versions
of the createValue function.
let value = await device.createValue({
name: 'Temperature',
permission: 'r',
template: Wappsto.ValueTemplate.TEMPERATURE_CELSIUS,
disableLog: true
});
There are some helper functions to create custom number, string, blob and xml values. To create a custom number value:
let value = await device.createNumberValue({
name: 'Value Name',
permission: 'rw',
type: 'Counter',
period: '1h',
delta: '1',
min: 0,
max: 10,
step: 1,
unit: 'count',
si_conversion: 'c',
disableLog: false,
initialState: {
data: 0,
timestamp: '2022-02-02T02:02:02Z',
},
});
To create a custom string value:
let value = await device.createStringValue({
name: 'String Value Name',
permission: 'rw',
type: 'debug',
max: 10,
encoding: 'debug string',
});
To create a custom blob value:
let value = await device.createBlobValue({
name: 'Blob Value Name',
permission: 'r',
type: 'binary',
max: 10,
encoding: 'binary',
});
To create a custom xml value:
let value = await device.createBlobValue({
name: 'XML Value Name',
permission: 'rw',
type: 'config',
namespace: 'seluxit'
xsd: 'https://xsd.com/test.xsd',
});
To send a new data point to wappsto, just call the report
function
on the value. If you omit the timestamp, it will get the curren time as timestamp.
It is possible to send number, string, boolean and objects.
await value.report(1);
await value.report('1', '2022-02-02T02:02:02Z');
await value.report(true);
await value.report({"my_data":"is updated"});
And to get the last reported data and timestamp.
let data = value.getReportData();
let timestamp = value.getReportTimestamp();
If you need to report multiple historical data, you can specify an array of data, timestamp pairs.
await value.report([
{ data: 1, timestamp: '2022-02-02T02:02:01Z' },
{ data: 2, timestamp: '2022-02-02T02:02:02Z' },
{ data: 3, timestamp: '2022-02-02T02:02:03Z' },
]);
If you do not want the current report state to change, you can use the sendLogReports
instead.
await value.sendLogReports([
{ data: 1, timestamp: '2022-02-02T02:02:01Z' },
{ data: 2, timestamp: '2022-02-02T02:02:02Z' },
{ data: 3, timestamp: '2022-02-02T02:02:03Z' },
]);
To receive request to refresh the value, register a callback on
onRefresh
. This can be triggered by an user request or an
automatic event from the library based on the period.
value.onRefresh((value) => {
value.report(1);
});
It is possible to get the type of the source of the refresh event. But you should not use this information to not send an update on the value. It is important to always send value updates on all refresh requests.
value.onRefresh((value, type) => {
console.log(`Got a refresh event from ${type}`);
});
And you can cancel this callback by calling cancelOnRefresh
.
value.cancelOnRefresh();
It is possible to save events in an event log for each value. Call
addEvent
on a value to create an event entry.
The possible values for level is:
await value.addEvent('error', 'something went wrong');
To request access to an existing object, you need to send a request. You can request a single object or multiple objects of the same type. To request access to multiple objects, specify the desired quantity after using the findByName('name', 3)
syntax. Alternatively, you can request access to all possible objects that match the query by calling findAllByName
. If you only require read access to the data, you can set the readOnly
parameter to true
.
To request access to a network with a specific name, utilize the findByName
function.
let oneNetwork = await Wappsto.Network.findByName('Network name');
let oneReadOnlyNetwork = await Wappsto.Network.findByName('Network name', 1, true);
let multipleNetworks = await Wappsto.Network.findByName('Network name', 3);
let allNetworks = await Wappsto.Network.findAllByName('Network name');
let allReadOnlyNetworks = await Wappsto.Network.findAllByName('Network name', true);
To request access to a device with a specific name, use findByName
.
let oneDevice = await Wappsto.Device.findByName('Device name');
let oneReadOnlyDevice = await Wappsto.Device.findByName('Device name', 1, true);
let multipleDevices = await Wappsto.Device.findByName('Device name', 3);
let allDevices = await Wappsto.Device.findAllByName('Device name');
let allReadOnlyDevices = await Wappsto.Device.findAllByName('Device name', true);
To request access to a device with a specific product, use findByProduct
.
let oneDevice = await Wappsto.Device.findByProduct('Product name');
let oneReadOnlyDevice = await Wappsto.Device.findByProduct('Product name', 1, true);
let multipleDevices = await Wappsto.Device.findByProduct('Product name', 3);
let allDevices = await Wappsto.Device.findAllByProduct('Product name');
let allReadOnlyDevices = await Wappsto.Device.findAllByProduct('Product name', true);
To request access to a value with a specific name, use findByName
.
let oneValue = await Wappsto.Value.findByName('Value name');
let oneReadOnlyValue = await Wappsto.Value.findByName('Value name', 1, true);
let multipleValues = await Wappsto.Value.findByName('Value name', 3);
let allValues = await Wappsto.Value.findAllByName('Value name');
let allReadOnlyValues = await Wappsto.Value.findAllByName('Value name', true);
To request access to a value with a specific type, use findByType
.
let oneValue = await Wappsto.Value.findByType('Type name');
let oneReadOnlyValue = await Wappsto.Value.findByType('Type name', 1, true);
let multipleValues = await Wappsto.Value.findByType('Type name', 3);
let allValues = await Wappsto.Value.findAllByType('Type name');
let allReadOnlyValues = await Wappsto.Value.findAllByType('Type name', true);
Enhance your control over the specific objects you request by utilizing advanced filters. Combine various keys in the filter to refine the returned objects. To find multiple objects of the same type, utilize an array with the values. The filter primarily consists of three main entry points: network, device, and value. A filter value can be a string, number, or an array of strings or numbers. Additionally, you can specify the operator used to compare attributes using an object with the 'operator' and 'value' keys.
{ operator: '==', value: 'test' }
The permitted operators are: '=', '!=', '==', '<', '<=', '>', '>=', '', '!'.
You can see the possible filters below.
const filter = {
network: {
name: '',
description: ''
},
device: {
name: '',
product: ['',''],
serial: '',
description: '',
protocol: '',
communication: '',
version: '',
manufacturer: ''
},
value: {
name: '',
permission: '',
type: '',
description: '',
period: '',
delta: '',
number: {
min: '',
max: '',
step: '',
unit: '',
si_conversion: ''
},
string: {
max: '',
encoding: '',
},
blob: {
max: '',
encoding: '',
},
xml: {
xsd: '',
namespace: ''
}
}
}
Once you've defined the filter, utilize it with the functions findByFilter
and findAllByFilter
on Network
, Device
, and Value
.
const filter = { value: { type: 'energy' }};
let oneValue = await Wappsto.Value.findByFilter(filter);
let allValues = await Wappsto.Value.findAllByFilter(filter);
You can also use the filter to exclude objects by providing a second filter to findByFilter
and findAllByFilter
. Specify the number of items to request with a third parameter. Additionally, indicate that the items are only needed for reading by setting the fourth parameter to true.
const filter = { value: { type: 'energy' }};
const omit_filter = { device: { name: 'Wrong' }};
let oneValue = await Wappsto.Device.findByFilter(filter, omit_filter);
let multipleValues = await Wappsto.Value.findByFilter(filter, omit_filter, 3);
let multipleReadOnlyValues = await Wappsto.Value.findByFilter(filter, omit_filter, 3, true);
let allValues = await Wappsto.Value.findAllByFilter(filter, omit_filter);
let allReadOnlyValues = await Wappsto.Value.findAllByFilter(filter, omit_filter, true);
To find devices under a network, you can call findDeviceByName
to
search for all devices with the given name.
let devices = network.findDeviceByName('Device name');
Or you can search for all devices with a given product.
let devices = network.findDeviceByProduct('Device product');
To find all values under a network or device, you can call
findValueByName
to search for all values with the given name.
let values = network.findValueByName('value name');
let values = device.findValueByName('value name');
Or you can find all values with a given type, by calling
findValueByType
.
let values = network.findValueByType('value type');
let values = device.findValueByType('value type');
To list all objects of a spefic type that you have access to use fetch
to get then all.
let networks = await Wappsto.Network.fetch();
If you already have access to some objects, you can retrieve them directly by their ID.
let network = Wappsto.Network.findById('655937ac-c054-4cc0-80d7-400486b4ceb3');
let device = Wappsto.Device.findById('066c65d7-6612-4826-9e23-e63a579fbe8b');
let value = Wappsto.Value.findById('1157b4fa-2745-4940-9201-99eee5929eff');
To send a new data point to another value, just call the control
function on the value.
const result = await value.control('1');
if (!result) {
console.warn('Failed to send control to device');
}
If you need to verify that the device send back a report, you can use
controlWithAck
to wait for the incoming report. It will return the
received value from the device.
const result = await value.controlWithAck('1');
switch (result) {
case undefined:
console.log('Failed to control device');
break;
case null:
console.log('Timeout while waiting for response');
break;
default:
console.log(`Received ${result} from the device`);
break;
}
And to get the last controlled data and timestamp.
let data = value.getControlData();
let timestamp = value.getControlTimestamp();
You can register onControl and onReport to receive events when the value changes.
value.onReport((value, data, timestamp) => {
console.log(`${value.name} changed it's report value to ${data}`);
});
value.onControl((value, data, timestamp) => {
console.log(`Request to change ${value.name} to ${data}`);
});
If you want the onReport callback to be called with the current data,
then you can set the ´callOnInit´ to true
when registering your
onReport
callback.
value.onReport(reportCallback, true);
And you can cancel these callbacks by calling cancelOnReport
and
cancelOnControl
.
value.cancelOnReport();
value.cancelOnControl();
If you want to load the newsiest data from the server manually, you can
call reload
on the model, to get the latest data for that model.
await device.reload();
If you want to also load all the children data, you should call
reload
with true
.
await device.reload(true);
To send an event to a device to get a new report data.
await value.refresh();
It is possible to load historical data for a value. Both report and
control data can be loaded using getReportLog
and
getControlLog
. Each function takes a LogRequest
object, where you
can define the parameters for the log service.
const historicalReportData = await value.getReportLog({});
const historicalControlData = await value.getControlLog({});
The LogRequest
object is defined like this:
Key | Type | Meaning |
---|---|---|
start | date | The start time for the log request |
end | date | The end time for the log request |
limit | number | The maximum number of items in the result |
offset | number | How many items that should be skipped before returning the result |
operation | string | What operation should be performed on the data before returning it |
group_by | string | What time interval should the log data be grouped by |
timestamp_format | string | The format the timestamp should be converted into |
timezone | string | If the timestamp should be converted to a timezone instead of UTZ |
order | string | Should the result be ordered ascending or descending |
order_by | string | The field that should be used to order by |
number | boolean | If only numbers should be returned in the log response |
It is possible to check if a network or device is online by calling
isOnline
.
if (network.isOnline()) {
console.log('Network is online');
}
if (device.isOnline()) {
console.log('Device is online');
}
You can also register a callback that are called every time the connection status changes.
network.onConnectionChange((network, status) => {
console.log(
`Network ${network.name} is now ${status ? 'online' : 'offline'}`
);
});
If you have a created your own device, you can set it online/offline
by calling setConnectionStatus
on the device. This will automatic create
a CONNECTION_STATUS
value if needed.
await device.setConnectionStatus(true);
To change the delta and period of a value you can call setDelta
or
setPeriod
.
value.setPeriod('300');
value.setDelta(2.2);
It is possible to remove an item that you have created by calling delete
on the item.
This will remove the item from the backend.
await value.delete();
It is possible to use the Analytic Backend to analyze the historical data of a value.
To convert the total energy value into a load curve value, call analyzeEnergy
with a start and end time on the value that you want to analyze.
const energy_data = await value.analyzeEnergy('2022-01-01T00:00:00Z', '2023-01-01T00:00:00Z');
It is possible to send messages to and from the background and the foreground. When you register a event handler, it will be called for each event send. The return value of your event handler is send back to the sender of the event.
Wappsto.fromBackground((msg) => {
console.log('Message from the background: ' + msg);
return 'Hello back';
});
Wappsto.fromForeground((msg) => {
console.log('Message from the foreground: ' + msg);
return 'Hello front';
});
let backgroundResult = await Wappsto.sendToBackground('hello');
console.log('Result from background: ' + backgroundResult);
let foregroundResult = await Wappsto.sendToForeground('hello');
console.log('Result from foreground: ' + foregroundResult);
If you want to send a message, but do not want a reply, you can use
signalBackground
.
await Wappsto.signalBackground('start');
await Wappsto.signalForeground('started');
If you do not want to receive anymore messages, you can cancel the event handler.
Wappsto.cancelFromBackground();
Wappsto.cancelFromForeground();
If you need to communicate with the background, it is a good idea to
wait for the background to be ready and have registered the
waitForBackground
handler. This can be do using the
waitForBackground
function.
const result = await Wappsto.waitForBackground();
if (result) {
console.log('The background is now ready');
}
It waits for 10 seconds, but this can be changed by giving it a new
timeout. And -1
to wait for ever.
const result = await Wappsto.waitForBackground(20);
It is possible to get notified when there are a change in the permissions. This is helpful when an user adds new items to the Wapp and the Wapp needs to show an updated list of items.
Wappsto.onPermissionUpdate(() => {
console.log('Permissions changed');
});
You can also stop getting notified again, by calling cancelPermissionUpdate
.
If the Ext Sync is enabled for the Wapp, a event handler for WebHooks can be registered. If you return something from your handler, it will be returned to the caller.
Wappsto.onWebHook((event, method, uri, headers) => {
console.log('Web Hook event', event);
return { status: "ok" };
});
The webhook url is https://wappsto.com/services/extsync/request/<wapp_token>
.
You can get the token from the value extSyncToken
.
console.log(`WebHook URL: https://wappsto.com/services/extsync/request/${Wappsto.extSyncToken}`);
It is also possible to change the return code byt returning a object with a body
and code
.
This also supports adding headers to the response.
Wappsto.onWebHook((event, method, uri, headers) => {
console.log('Web Hook event', event);
return {
code: 201,
headers: {
'Content-Type': 'application/json',
},
body: { status: "ok" };
};
});
And if you want to cancel the web hook event handler, you can call
cancelWebHook
.
Wappsto.cancelOnWebHook(handler);
You can turn on Ext Sync
support using the wappsto-cli
using the
npx wapp configure
command.
It is possible to store configuration parameters and other information
in the Wapp Storage. This data is persisted on Wappsto and can be read
from the foreground and background wapp. The data can be reload from
the server by calling reload
function. The data can be deleted by
calling the reset
function. A callback can also be registered to be
notified when the storage is updated. It is possible to set a single
or multiple data into the storage. In the same manner it is possible
to get and remove multiple keys at the same time.
let storage = await Wappsto.wappStorage();
// Signal when storage is changed
storage.onChange(() => {
console.log('Storage is updated');
});
// Set new data into the store
await storage.set('key', 'item');
await storage.set({key2: 'item 2', key3: 'item 3'});
// Get data from the store
let data = storage.get('key');
let data2 = storage.get(['key2', 'key3']);
// Reload the data from the server
await storage.reload();
// Remove data from the store
await store.remove('key1');
await store.remove(['key2', 'key3']);
// Delete all the saved data
await storage.reset();
It is possible to store secret information in the Storage from the background part of the wapp. This information is only available for the background wapp and can't be accessed from the frontend part of the wapp.
await storage.setSecret('key', 'item');
const secret_data = storage.getSecret('key', 'item');
await storage.removeSecret('key', 'item');
To get an already created OAuth token, call the getToken with the name
of the oauth. If there is no token generated yet, a Request Handler
needs to be supplied, so that the library can call it with the url
that the user needs to visit, in order to generate the OAuth Token.
try {
let token = await Wappsto.OAuth.getToken('oauth name', (url) => {
console.log(`Please visit ${url} to generate the OAuth token`);
});
console.log('OAuth Token', token);
} catch (e) {
console.log('Failed to get OAuth token', e);
}
It is possible to send custom notification to the main Wappsto.com dashboard.
await Wappsto.notify('This is a custom notification from my Wapp');
You can also send an email to your self by calling sendMail
.
await Wappsto.sendMail({
subject: 'Mail with information',
body: '<h1>Hello from the Wapp</h1><p>Here is some more information</p>',
from: 'My New Wapp',
});
It is also possible to send a SMS to your self, if your phone number is verified.
await Wappsto.sendSMS('Simple SMS from your Wapp');
To build a relationship graph of your data, you need to define the ontology.
To add a relationship between two objects, you call createEdge
with
the relationship that you want to define.
const edge = await startNode.createEdge({ relationship: 'child', to: endNode });
You can also supply more information about the edge.
const fullEdge = await startNode.createEdge({
relationship: 'child',
to: endNode,
name: 'My Edge',
description: 'This is my first edge',
data: { msg: 'My own data for this edge' },
});
If you need a virtual object to create an edge to or from, you can
create a new node. To create a node in the graph, you call the createNode
function.
const startNode = await Wappsto.createNode('start node');
When you have created your ontology, you can transverse
the graph
and find 'leafs' in the graph. You need to define a path to
follow. There are 3 special characters in the path, that have special
meaning. .
is used to separate each element in the path. *
is used
as matching any relationship. |
is used as an 'or' between relationships.
const leafs = node.transverse('*.contains|child.*');
When calling transverse
the result is the leaf at the end of the
path. But if you want all the nodes that are found along the path, you
can set 'getAll' to true when calling transverse
.
const allNodes = node.transverse('*.contains|child.*', true);
To load all the ontology edges from an object, you can call getAllEdges
.
const edges = await node.getAllEdges();
To the models that the edge points to is available in the models
attribute.
edges.forEach((edge) => {
edge.models.forEach((model) => {
console.log(`The edge points to ${model.name}`);
});
});
The edges are only loaded once, so if you need to refresh the edges from
the backend, you need to force an update by calling getAllEdges
with true.
const edges = await node.getAllEdges(true);
If not all models can be loaded that the edge points to, the failed models are
stored in failedModels
on the edge.
const edges = await node.getAllEdges();
console.log(edges[0].failedModels);
To remove an edge, you can just call delete
on the edge, but if you
want to remove the whole branch, you can call deleteBranch
.
This will remove all edges and nodes under this node or edge.
await startNode.deleteBranch();
The debug log from the background wapp is enabled by default, but can
be turned off by calling stopLogging
.
Wappsto.stopLogging();
It is possible to send your own requests to wappsto by using the
request
object in wappsto.
let networks = await Wappsto.request.get('/network');
await Wappsto.request.post('/network', { name: 'Network Name' });
To get the current version of the installed application, call getWappVersion
.
const version = await Wappsto.getWappVersion();
console.log(`Current Installed Wapp Version: ${version}`);
It is possible to change some of the behavior of the library using config.
It is possible to disable the validation of the input parameters, by changing it in the config. It can be 'none' or 'normal'. The default validation is 'normal'.
Wappsto.config({
validation: 'none',
});
It is possible to change from the default 10 times the stream will try to reconnect in case of connection errors.
Wappsto.config({
reconnectCount: 3,
});
It is possible to get a lot of extra debug information about when the library is doing by enabling debug options.
Wappsto.config({
debug: true,
requests: true,
stream: true,
});
FAQs
[](https://github.com/Wappsto/wappsto-wapp/actions/workflows/main.yml) [![codecov](https://codecov.io/gh/Wappsto/javascript-wappsto-wapp/branch/main/graph/badge.svg?token=Y
The npm package wappsto-wapp receives a total of 256 weekly downloads. As such, wappsto-wapp popularity was classified as not popular.
We found that wappsto-wapp demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncovered a malicious PyPI package exploiting Deezer’s API to enable coordinated music piracy through API abuse and C2 server control.
Research
The Socket Research Team discovered a malicious npm package, '@ton-wallet/create', stealing cryptocurrency wallet keys from developers and users in the TON ecosystem.
Security News
Newly introduced telemetry in devenv 1.4 sparked a backlash over privacy concerns, leading to the removal of its AI-powered feature after strong community pushback.