{"id":16670502,"url":"https://github.com/hlapp/wirelesstags-js","last_synced_at":"2025-04-09T19:41:39.859Z","repository":{"id":51106913,"uuid":"74937704","full_name":"hlapp/wirelesstags-js","owner":"hlapp","description":"JavaScript API for the Wireless Sensor Tag platform","archived":false,"fork":false,"pushed_at":"2021-11-08T19:33:41.000Z","size":803,"stargazers_count":6,"open_issues_count":7,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-23T21:35:51.295Z","etag":null,"topics":["iot","sensor","wireless-sensor-tag"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hlapp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-11-28T04:37:06.000Z","updated_at":"2021-05-19T00:36:07.000Z","dependencies_parsed_at":"2022-09-06T07:02:40.494Z","dependency_job_id":null,"html_url":"https://github.com/hlapp/wirelesstags-js","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hlapp%2Fwirelesstags-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hlapp%2Fwirelesstags-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hlapp%2Fwirelesstags-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hlapp%2Fwirelesstags-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hlapp","download_url":"https://codeload.github.com/hlapp/wirelesstags-js/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247704571,"owners_count":20982298,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["iot","sensor","wireless-sensor-tag"],"created_at":"2024-10-12T11:38:38.485Z","updated_at":"2025-04-09T19:41:39.835Z","avatar_url":"https://github.com/hlapp.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/hlapp/wirelesstags-js.svg?branch=master)](https://travis-ci.org/hlapp/wirelesstags-js)\n[![npm](https://img.shields.io/npm/v/wirelesstags.svg)](https://www.npmjs.com/package/wirelesstags)\n[![npm](https://img.shields.io/npm/dt/wirelesstags.svg)](https://www.npmjs.com/package/wirelesstags)\n[![david-dm](https://david-dm.org/hlapp/wirelesstags-js.svg)](https://david-dm.org/hlapp/wirelesstags-js)\n[![david-dm](https://david-dm.org/hlapp/wirelesstags-js/dev-status.svg)](https://david-dm.org/hlapp/wirelesstags-js?type=dev)\n\n# wirelesstags - JavaScript API for the Wireless Sensor Tags platform\n\nAims to provide a well-structured API to the [Wireless Sensor Tag]\nplatform by interfacing with its [JSON Web Service API]. It is\nprimarily intended, designed, and tested for server-side use through\nNodeJS. (However, making it usable within a browser is a future goal,\nand corresponding contributions are welcome too.)\n\n## Installation and setup\n\n```sh\n$ npm install wirelesstags\n```\n\nThe library (specifically, the `platform.signin()` method, see\nbelow) will need authentication information. The library supports two\ndefault ways to pick up this information:\n\n1. A file `$HOME/.wirelesstags` in JSON format, with the necessary\n   authentication information (currently keys `username` and\n   `password`). This file should obviously be readable only by the\n   user running the app.\n2. Environment variables `WIRELESSTAG_API_USER` and\n   `WIRELESSTAG_API_PASSWORD`, if set, will override whatever is found\n   in the default options file.\n\nIt is strongly recommended to create a separate account as a \"limited\nuser\" for using this library rather than your main account(s) at\nWirelesstag.com. This makes it easy to change the password or delete\nthe account altogether if the password happens to leak out, and allows\ncontrolling which tag managers and tags are visible to the\naccount. Note also that wirelesstag.com stores your password in clear\ntext (you can verify by recovering it), and hence never use a password\nthere that you use anywhere else.\n\n## Usage\n\n### Principle objects\n\nThe principle object hierarchy is the following:\n\n* `WirelessTagPlatform`: Top-level object representing the cloud\n  interface to the platform. Emits `discover` events for\n  `WirelessTagManager` instances upon calling `platform.discoverTagManagers()`.\n\n* `WirelessTagManager`: Object representing a [Tag Manager].\n   Discovered through the platform object. Emits `discover` events for\n   Wireless Tags associated with the tag manager upon calling\n   `tagManager.discoverTags()`.\n\n* `WirelessTag`: Object representing a [Wireless Tag]. Discovered\n  through the tag manager object. Tags have sensor capabilities, which\n  can be queried using `tag.hasHumiditySensor()` etc methods, or as an\n  array of strings via `tag.sensorCapabilities()`. Sensor objects can\n  be discovered by calling `tag.discoverSensors()`, which emits a\n  `discover` event for each newly found sensor. They can be iterated\n  over using `tag.eachSensor()`, which takes a callback.\n\n* `WirelessTagSensor`: Object abstracting a sensor that is part of a\n  Wireless Tag. Sensor objects are of a type (`sensor.sensorType`),\n  and usually have a `reading` and an `eventState` property. They\n  can be armed or disarmed (`sensor.arm()` and `sensor.disarm()`), and\n  their monitoring and notification configuration is available as\n  properties of the object returned by `sensor.monitoringConfig()`.\n\n### Initialize platform, connect to cloud, and discover tag managers\n\nThe platform object can be created using its constructor, or using the\nstatic method `WirelessTagPlatform.create()`.\n\n```javascript\nvar WirelessTagPlatform = require('wirelesstags');\n\n// Passing a config object is optional. Default for apiBaseURI is\n// https://www.mytaglist.com\nvar platform = new WirelessTagPlatform({ apiBaseURI: 'https://my.wirelesstag.com' });\n```\n\nWhen using the static `create()` method, it will try to load\nconfiguration options from `~/.wirelesstags`, or from the environment:\n\n```javascript\nvar WirelessTagPlatform = require('wirelesstags');\n\nvar platform = WirelessTagPlatform.create();\n```\n\nPlatform instances emit a `connect` event after successful\nlogging in, and a `discover` event for each tag manager object. The\n`signin()` and `discoverTagManagers()` methods also return promises,\nthe latter with an array of tag manager objects.\n\nDefine or obtain connection options:\n\n```javascript\nvar opts = { username: 'foo@bar.com', password: 'supersecret' };\n// or load from default configuration file or environment variables:\nopts = WirelessTagPlatform.loadConfig();\n```\n\n#### Connect and discover tag managers using event handlers\n\n```javascript\nplatform.on('connect', () =\u003e {\n    console.log(\"connected to Wireless Tag cloud\");\n    platform.discoverTagManagers();\n});\nplatform.on('discover', (tagManager) =\u003e {\n    console.log(\"found tag manager\", tagManager.name, tagManager.mac);\n});\n// once the listeners are set up we can connect\nplatform.signin(opts)\n```\n\nA platform instance (since v0.6.0) caches tag manager objects\nresulting from a call to `discoverTagManagers()`. Subsequent calls\nwill not update properties of these objects, but emit `discover`\nevents only for newly found (not previously cached) tag managers. This\nallows an application to scan periodically for new tag managers,\nwithout receiving `discover` events redundantly for the same objects.\n\n#### Connect and discover tag managers using returned promises\n\n```javascript\nplatform.connect(opts).then(() =\u003e {\n    return platform.discoverTagManagers();\n}).then((tagManagers) =\u003e {\n    tagManagers.forEach((tagManager) =\u003e {\n        console.log(\"found tag manager\", tagManager.name, tagManager.mac);\n    });\n});\n```\n\nThe method always promises _all_ tag manager objects found (hence the\nnumber of `discover` events fired will only on the first call be the\nsame as the number of objects promised). Since v0.6.0, if called\nrepeatedly the objects promised for tag managers discovered previously\nwill be the same (but with updated properties), allowing an\napplication to scan periodically for new tag managers without losing\nthe application's state of prviously returnd objects.\n\n### Discovering tags and their sensors\n\nThe tag manager object emits `discover` events for each tag associated\nwith it after starting discovery by calling `tagManager.discoverTags()`.\nIn the same way, tag objects emit `discover` events for each of their\nnewly found sensors after initiating discovery with\n`tag.discoverSensors()`.\n\nThe discovery methods also promise arrays of tags and sensors,\nrespectively. Either approach (promises or events) can be used.\n\nThe `tag.discoverSensors()` method always promises an array of _all_\nits sensors, whereas it emits `discover` events _only_ for newly found\nsensors. Subsequent `tag.discoverSensors()` calls will promise the\nsame sensor objects (unless there were new sensors, but the current\ngeneration of Wireless Tags cannot dynamically gain sensors).\n\nIn contrast, `tagManager.discoverTags()` always emits the same number\nof events as there are elements in the promised array of tag objects,\nand the tag objects are always new objects, because tag manager\nobjects don't cache their associated tag objects. Indeed in practice\ntags can be dynamically associated with or disassociated from tag\nmanagers.\n\n#### Discovering tags and sensors using event handlers\n\n```javascript\ntagManager.on('discover', (tag) =\u003e {\n    console.log(\"Tag\", tag.name, \"(slaveId=\" + tag.slaveId + \")\", tag.uuid);\n    console.log(\".. last updated\", tag.lastUpdated());\n    tag.on('discover', (sensor) =\u003e {\n        console.log(\"..\", sensor.sensorType, \"of\", sensor.wirelessTag.name);\n        console.log(\"    reading:\", sensor.reading);\n        console.log(\"    state:\", sensor.eventState);\n        console.log(\"    armed:\", sensor.isArmed());\n    });\n    tag.discoverSensors();\n});\ntagManager.discoverTags();\n```\n\n#### Discovering tags and sensors using promises\n\n```javascript\ntagManager.discoverTags().then((tags) =\u003e {\n    tags.forEach((tag) =\u003e {\n        console.log(\"Tag\", tag.name, \"(slaveId=\" + tag.slaveId + \")\", tag.uuid);\n    });\n    return Promise.all(tags.map((tag) =\u003e { return tag.discoverSensors(); }));\n}).then((sensorLists) =\u003e {\n    sensorLists.forEach((sensors) =\u003e {\n        var tag = sensors[0].wirelessTag;\n        console.log(\"Sensors of tag\", tag.name, tag.uuid);\n        sensors.forEach((sensor) =\u003e {\n            console.log(\"..\", sensor.sensorType, \"sensor\");\n            console.log(\"    reading:\", sensor.reading);\n            console.log(\"    state:\", sensor.eventState);\n            console.log(\"    armed:\", sensor.isArmed());\n        });\n    });\n});\n```\n\n#### Discovering tags and sensors directly from platform\n\nSince v0.6.0, tag objects can be discovered directly in one go from\nthe platform object, without first finding the tag manager objects.\n\nIn terms of performance as determined by the sequence of cloud API\ncalls, there is no difference to finding the tag managers first if\nonly one tag manager is accessible to the connected account. However,\nin the case of multiple tag managers under the account, the difference\ncan be notable (because currently the cloud API does not support\nfiltering tags by tag manager at the server).\n\n```javascript\nplatform.discoverTags().then((tags) =\u003e {\n    tags.forEach((tag) =\u003e {\n        console.log(\"Tag\", tag.name, \"of\", tag,wirelessTagManager.name,\n                    \"(slaveId=\" + tag.slaveId + \")\", tag.uuid);\n    });\n    // the following may need rate-limiting if there are many tags\n    // (e.g., see package p-limit for rate-limiting promises)\n    return Promise.all(tags.map((tag) =\u003e { return tag.discoverSensors(); }));\n}).then((sensorLists) =\u003e {\n    sensorLists.forEach((sensors) =\u003e {\n        var tag = sensors[0].wirelessTag;\n        console.log(\"Sensors of tag\", tag.name, tag.uuid);\n        sensors.forEach((sensor) =\u003e {\n            console.log(\"..\", sensor.sensorType, \"sensor\");\n            console.log(\"    reading:\", sensor.reading);\n            console.log(\"    state:\", sensor.eventState);\n            console.log(\"    armed:\", sensor.isArmed());\n        });\n    });\n});\n```\n\n#### Finding a specific tag\n\nEach tag is uniquely identified by a UUID (available as `tag.uuid`). This\ncould be used to pass a query to `platform.discoverTags()`:\n\n```js\nvar uuidOfTag = 'DESIRED UUID VALUE';\nplatform.discoverTags({ uuid: uuidOfTag }).then((tags) =\u003e {\n    if (tags.length === 0) throw new Error(\"tag not found\");\n    return tags[0].discoverSensors();\n}).then((sensorList) =\u003e {\n    var tag = sensorList[0].wirelessTag;\n    console.log(\"Sensors of tag\", tag.name, tag.uuid);\n    sensorList.forEach((sensor) =\u003e {\n        console.log(\"..\", sensor.sensorType, \"sensor\");\n        console.log(\"    reading:\", sensor.reading);\n        console.log(\"    state:\", sensor.eventState);\n        console.log(\"    armed:\", sensor.isArmed());\n    });\n});\n```\n\nIt should be noted that this has no performance advantage over filtering the\nlist of tag objects from `platform.discoverTags()`, because the\n[JSON Web Service API] has no server-side support for querying by UUID.\n\nAnother way to uniquely (at a moment in time) specify a tag is by tag manager\n(as identified by its MAC) and the tag's `slaveId` (a consecutive numbering\nfor the tags associated with a tag manager):\n\n```js\nvar MAC = '123456789ABCDEF' // replace with MAC of tag manager;\nvar slaveId = 12; // replace with slave ID of desired tag;\nplatform.findTagManager(MAC).then((tagMgr) =\u003e {\n    if (! tagMgr) throw new Error(\"tag manager not found\");\n    return tagMgr.findTagById(slaveId); // rejects if not found\n}).then((tag) =\u003e tag.discoverSensors()).then((sensorList) =\u003e {\n    var tag = sensorList[0].wirelessTag;\n    console.log(\"Sensors of tag\", tag.name, tag.uuid);\n    sensorList.forEach((sensor) =\u003e {\n        console.log(\"..\", sensor.sensorType, \"sensor\");\n        console.log(\"    reading:\", sensor.reading);\n        console.log(\"    state:\", sensor.eventState);\n        console.log(\"    armed:\", sensor.isArmed());\n    });\n}).catch((err) =\u003e console.error(err.stack ? err.stack : err));\n```\n\nFor an account with access to many tags this may perform noticeably better\nthan filtering by UUID, because once the tag manager is found (and the number\nof tag managers is likely at least an order of magnitude smaller than the\nnumber of tags), obtaining the tag's data by `slaveId` is supported server-side.\n\n#### Accessing sensors through tag object\n\nOnce the promise returned from `tag.discoverSensors()` is fulfilled,\nsensor objects can also be accessed through the methods of the tag\nobject (because tag objects cache their sensors, and in practice\nsensors can't be associated with or disassociated from a tag\ndynamically). Either use `tag.eachSensor()` with a callback that\naccepts a sensor object, or use each sensor's individual accessor\nproperty, which are all named `zzzzSensor`, where zzzz is the type of\nsensor, for example `tag.lightSensor`. The method `tag.sensorCapabilities()`\nreturns an array of strings denoting the possible `zzzz` values.\n\n#### Changing the temperature unit\n\nBy default temperature sensors give their readings and monitoring thresholds\nin ºC. The unit can be changed by setting `sensor.monitoringConfig().unit`:\n\n```js\nif (sensor.sensorType === \"temp\") {\n    // temperature reading and thresholds in ºC:\n    sensor.monitoringConfig().unit = \"degC\";\n    // temperature reading and thresholds in ºF:\n    sensor.monitoringConfig().unit = \"degF\";\n    // if desired, the change can be saved to become persistent:\n    sensor.monitoringConfig().save().then((mconfig) =\u003e {\n        console.log(\"saved monitoring config of temp of\", sensor.wirelessTag.name);\n        console.log(\"... unit is now\", mconfig.unit);\n    });\n}\n```\n\n### Updating sensor values and tag information\n\nUpdating the tag object's data also updates its sensors. `tag.update()`\nupdates a tag object's data from the cloud, and `tag.liveUpdate()` requests\nthat the tag immediately post its latest information.\n\nTo regularly update a tag object's data whenver the tag posts its latest\ninformation, a loop can be started with `tag.startUpdateLoop()`, and\nstopped by `tag.stopUpdateLoop()`.\n\n### Promises versus Callbacks\n\nThe library attempts to support both returning Promises from\nAPI-calling asynchronous functions, and the traditional callback\nmechanism.\n\nCallbacks are generally called with an error as the first argument if\none occurred, and as the second argument with an object that has keys\n`object` (the object in which the operation was performed), and\n`value` (the resulting value from that operation) if the operation had\na result that is not saving or updating a property value of the\nobject. For example, in `platform.isConnected(cb)`, `cb` will be\ncalled with `{ object: platform, value: false }` if the instance\nwasn't yet connected.\n\nIt is considered a bad idea, and not supported (even if it may often\nwork) to mix passing callbacks _and_ using the returned Promises.\n\nNote that callback behaviour is not currently tested as part of the\ntest suite, so there could be bugs.\n\n### Full documentation\n\nAs of v0.7.0, API documentation is fairly complete:\n\n* [Online API documentation](http://lappland.io/wirelesstags-js/wirelesstags/0.7.1)\n* See the [`examples/`](https://github.com/hlapp/wirelesstags-js/tree/release-v0.7.1/examples)\n  directory for tutorial scripts that give basic, but fully working\n  demonstrations of how the library can be used.\n\nThis library should be considered beta. Use at your own peril. Aside from the\nAPI documentation, consider looking at the (fairly extensive) test suite for\nguidance.\n\n## How to support\n\nAside from reporting issues and contributing pull requests, if you\nplan to buy from Wireless Sensor Tag, please consider using\n[this link](https://goo.gl/GxwQbZ) to their website. If more than 10\npeople do so, and some end up buying, I stand to receive a discount on\na past purchase of mine, which will allow me to buy other types of\ntags in the future and support those too. (See [#45] for an example.)\n\n## License\n\nAvailable under the [MIT License](LICENSE).\n\n[Wireless Sensor Tag]: http://wirelesstag.net\n[JSON Web Service API]: http://mytaglist.com/media/mytaglist.com/apidoc.html\n[Tag Manager]: http://wirelesstag.net/specs.html#manager\n[Wireless Tag]: http://wirelesstag.net/specs.html#tag\n[#45]: https://github.com/hlapp/wirelesstags-js/pull/45#issuecomment-354493269\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhlapp%2Fwirelesstags-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhlapp%2Fwirelesstags-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhlapp%2Fwirelesstags-js/lists"}