Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adapter API #1

Closed
benfrancis opened this issue Mar 23, 2017 · 17 comments
Closed

Adapter API #1

benfrancis opened this issue Mar 23, 2017 · 17 comments
Labels
Milestone

Comments

@benfrancis
Copy link
Member

benfrancis commented Mar 23, 2017

Eventually we'd like it to be possible to write adapters (e.g. ZigBee adapter, Z-Wave adapter) in different programming languages, have them run in their own process, and talk to them via some kind of IPC mechanism.

But for our initial NodeJS prototype we’ve discussed having some kind of uniform JavaScript interface for the web app to talk to adapters.

Based on the Web Thing REST API which will be a layer above this JavaScript API, my first thoughts on the what basic underlying features of the adapter layer might be are:

Adapter

  • discoverDevices() // List available or connected devices so we can compare with list of devices already added
  • connectDevice() // Pair with the device and/or get a connected device's details to add it to the gateway database
  • disconnectDevice() // Unpair device if such a mechanism exists for this adapter

Thing

  • getProperty() // Get a property value by its name
  • setProperty() // Set a property value by its name
  • executeAction() // What kinds of functions can we execute on ZigBee/Z-Wave devices?
  • subscribe() // What kinds of events or changes we can listen or subscribe to from ZigBee/Z-Wave devices?

Does this make sense? What have I missed?

@benfrancis benfrancis modified the milestone: Q2 2017 Mar 23, 2017
@benfrancis
Copy link
Member Author

Note that @dhylands added a repo to experiment with the adapter design https://github.com/moziot/adapter

@dhylands
Copy link
Contributor

Perhaps we should also have getPropertyDescription which returns the description information (as JSON) that goes into the thing?

I'd be inclined to call connect/disconnect pair and unpair.

ZWave sends events for things like the following:

  • a node was added or removed
  • a value changed (i.e. door switch)

@benfrancis
Copy link
Member Author

Perhaps we should also have getPropertyDescription which returns the description information (as JSON) that goes into the thing?

I think that would be more like getThing() which would return a full Thing Description including the property definitions. I can't think of a use case for needing to get the property description for a single Property (as opposed to the property value) after a Thing has been added to the gateway?

I'd be inclined to call connect/disconnect pair and unpair.

Works for me.

ZWave sends events for things like the following:
a node was added or removed
a value changed (i.e. door switch)

The latter sounds like something we'd want to subscribe to. The former sounds like something the adapter should persist until the web app queries it with discoverDevices() ?

@benfrancis
Copy link
Member Author

...I imagined the Thing Description would be generated when a Thing is added to the gateway. Not sure whether the adapter should have to know about the Web Thing description format used by the REST API or whether that should be generated one layer up, from metadata retrieved from the adapter at add time.

@fabricedesre
Copy link

Are all these methods sync or async?
Eg. are we guaranteed that:

thing.setProperty("prop", value);
assert_eq(thing.getProperty("prop"), value);

@dhylands
Copy link
Contributor

Perhaps we should also have getPropertyDescription which returns the description information (as JSON) that goes into the thing?

I'd be inclined to call connect/disconnect pair and unpair.

ZWave sends events for things like the following:

  • a node was added or removed
  • a value changed (i.e. door switch)

@benfrancis
Copy link
Member Author

Are all these methods sync or async?

That's a very good question, at both layers:

  • Adapter Interface - Does a setProperty() method call not return until a property has been successfully set?
  • Web Thing API - Does an HTTP PUT request not get a response until a property has been successfully set?

On the adapter interface setProperty() could return a Promise which resolves once the property has been set, so as not to block the thread.

On the Web Thing API an HTTP PUT request on a property could get a 202 Accepted response to say the request has been accepted but not yet processed. But then you'd need another mechanism (like polling, webhooks or WebSockets) to let the front end know if and when the change has actually taken place.

I think my preference might be to have the adapter interface return Promises but have the REST API wait for the promise to resolve before sending an HTTP response, timing out with an error. Unless there turn out to be very long response times, in which case we may need to try another approach.

I also remember someone suggested at one point that a boolean property should actually have four states: on, off, turningOn, turningOff.

@benfrancis
Copy link
Member Author

Perhaps we should also have getPropertyDescription which returns the description information (as JSON) that goes into the thing?

It would maybe be convenient if the adapter could provide a description of a device in the Web Thing Description format, but isn't necessary. I imagine the Thing Description being generated by the back end of the web app using Express "views" and the metadata stored in the gateway's database. In my mind the adapter interface exists as a layer below that.

If we have mappings of ZigBee and Z-Wave device types to Web Thing Types then the web app back end should be able to generate a Web Thing Description using some basic pieces of metadata from the adapter.

Once a Web Thing Description has been persisted, we shouldn't need to get individual property definitions from the adapter again, unless the capabilities of the device changes and we need to update the Thing Description.

I'd be inclined to call connect/disconnect pair and unpair.

Works for me.

ZWave sends events for things like the following:
a node was added or removed
a value changed (i.e. door switch)

Value changes are something which the front end should be able to subscribe to via the Web Thing API. Added/removed nodes should probably be persisted by the adapter so they can be queried when the front end calls discover().

@dhylands
Copy link
Contributor

Perhaps we should also have getPropertyDescription which returns the description information (as JSON) that goes into the thing?

It would maybe be convenient if the adapter could provide a description of a device in the Web Thing Description format, but isn't necessary. I imagine the Thing Description being generated by the back end of the web app using Express "views" and the metadata stored in the gateway's database. In my mind the adapter interface exists as a layer below that.

I guess the adapter has to generate something. ZWave knows the name of a node (which would map to a thing), and each node can have multiple values (properties). For each value ZWave knows the type (Bool., Byte, Decimal, Int, Short, String, Binary), value, min, max, units, label, help string, a polling frequency, whether its readable or writable, and whether its ever been set or not. Some things, like the min/max, units, label, help string, and polling frequency come from the ZWave XML files and not from the actual device.

In ZWave the Node (thing) also has properties, which include name, location, manufacturer id/name, product id/name, and a bunch of other minor attributes. The name and location are set by the user of the API. The other fields come from the XML file.

So we could generate a dictionary with what attributes of the above are known and leave it to the layer above the adapter to make the actual description.

@dhylands
Copy link
Contributor

Given that these devices are on a mesh network over RF, there will definitely be a delay between the time a value is set and the time it actually gets set. In some cases (ZWave) we'll get a value-changed notification to indicate that the value was actually set. In other cases (X10) there is no way to query the device, so its fire and forget.

@benfrancis
Copy link
Member Author

So we could generate a dictionary with what attributes of the above are known and leave it to the layer above the adapter to make the actual description.

Sounds good to me!

@fabricedesre
Copy link

I also remember someone suggested at one point that a boolean property should actually have four states: on, off, turningOn, turningOff.

Right. I think the idea here was that if you call bulb.setProperty("color", "blue"), it returns immediately with an opaque task identifier and the current status (here it could be "changing_color"). Once the property change is actually done, fire an event with the task id and the new state.

I think that model makes sense, and is very generic at the adapter level where even getProperty and setProperty could be implemented in terms of executeAction("setProperty"|"getProperty", ...). At the REST level I'm not so convinced... as you mentionned we could have the GET request wait for some time before bailing out, but it still means that the client needs to support the "I receive a task id and wait for an event" case.

@dhylands
Copy link
Contributor

And there are devices, like window blinds, which will take several, or possibly even tens of seconds to change states.

@fabricedesre
Copy link

Yes, the blind is a good example where you even may want to cancel/stop the current task.

@benfrancis
Copy link
Member Author

I think that model makes sense, and is very generic at the adapter level where even getProperty and setProperty could be implemented in terms of executeAction("setProperty"|"getProperty", ...). At the REST level I'm not so convinced... as you mentionned we could have the GET request wait for some time before bailing out, but it still means that the client needs to support the "I receive a task id and wait for an event" case.

Hmm, yes that might make sense at the adapter level. But if getting and setting properties was done using action requests at the REST level then it would kind of make the property resources redundant and would be more of an RPC API than a REST API. May require some thought. I'm sure there's prior art we can look to here for examples.

It could be that the Web Thing API offers two options - a set and forget approach with REST and more of a pub-sub style interface over WebSockets where the socket is kept open.

@benfrancis
Copy link
Member Author

@dhylands wrote:

I guess the adapter has to generate something. ZWave knows the name of a node (which would map to a thing), and each node can have multiple values (properties). For each value ZWave knows the type (Bool., Byte, Decimal, Int, Short, String, Binary), value, min, max, units, label, help string, a polling frequency, whether its readable or writable, and whether its ever been set or not. Some things, like the min/max, units, label, help string, and polling frequency come from the ZWave XML files and not from the actual device.

In ZWave the Node (thing) also has properties, which include name, location, manufacturer id/name, product id/name, and a bunch of other minor attributes. The name and location are set by the user of the API. The other fields come from the XML file.

So we could generate a dictionary with what attributes of the above are known and leave it to the layer above the adapter to make the actual description.

I wonder how much of this information actually needs passing up the stack.

I've proposed a discoverDevices() method in issue #18 which just returns an id (a UUID generated by the adapter), a type (one of the standard types from the Web Thing specification) and a user friendly name for each device.

If enough can be standardised in thing schemas (and their protocol bindings) then this might be all the information that's needed to for the web app back end to generate a Thing Description from a standard template.

It may only be if there are custom device types or third party schemas that the gateway doesn't yet support (and can't be represented by standard schemas or a combination of multiple schemas) that we may need to pass additional information up the stack, but I'm not sure how far we'd want to go in trying to automatically generate a UI for potentially endless permeations vs. just display some generic Thing representation and leave the custom functionality down to third party applications.

@fabricedesre:

Yes, the blind is a good example where you even may want to cancel/stop the current task.

Thinking about it more, I think this is an example where you'd send an action request rather than just set a property. The request then gets its own ID which can be cancelled via a DELETE. Simpler and more instantaneous actions like turning an LED on and off or setting a thermostat threshold temperature could just be done with properties.

@benfrancis
Copy link
Member Author

We have a basic adapters API now so I'm going to close this epic bug. Further enhancements will be tracked in follow ups.

benfrancis pushed a commit to benfrancis/gateway that referenced this issue Sep 23, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants