Design and realization of composable web services for home automation Elective in Software and Services Mario Caruso What we are going to see • Brief introduction to home automation systems. • How to make home devices really interoperable • DPWS as a framework for dynamic web services • Implentation of a very simple web service for controlling a lamp • Practical demonstration Smart Home [bus] Devices’ heterogeneity • Home automation system A + B Making devices homogeneus • Devices have to speak the same language. • We must abstract a device from its specific protocol. • Each device has a software module (proxy) able to communicate with it: – Send commands – Listen to sensed events turnOff() Protocol independent Light B Proxy Light interface Light A Proxy Protocol A implementation Light actuator (System A) turnOn( ) Protocol dependent B bus A bus Light A Proxy turnOn( ) turnOff() Light interface Light B Proxy Protocol B implementation Light actuator (System B) SOA • A proxy exposes device’s functionalities as Web Services • Web service pros: – Autonomous • The lifecycle of a proxy is independent from the others • Self-contained functionalities – Interoperable • Abstracts the interface from the implementation (loosely coupled) – Language and platform independent – Remotely invocable – Composable Orchestrator sensor actuator Orchestrator Proxy 1 Proxy 2 Proxy 3 Proxies simply act as servers Proxy 4 Proxy 5 Service implementation • In the next slides we will see how to design and implement services able to control home automation devices. • We will implement services by using the DPWS stack. In particular we will use JMEDS libraries. – http://ws4d.e-technik.uni-rostock.de/jmeds/ What is DPWS • DPWS = Devices Profile for Web Services • a profile is a set of guidelines for how to use Web Services technologies for a given purpose or application. • DPWS is a lightweight subset of WS implementations particularly suited for constrained resource devices. DPWS Capabilities • • • • Discover devices and their related services Send/Receive messages to/from devices Describe WS (through WSDL etc.) Subscribe and receive events notifications Rich service description as a contract • When designing/developing a service we start from its rich service description. • The rich service description is composed by three files: – The variable type declaration – The variable instance declaration – The behaviour description • In particular the developer must implement the transition system that describes both the conversational behaviour and the physical behaviour. Reminder • How the conversational state differs from the value of a variable? – The conversational state gives an indication about the actions that can be invoked on a service in a specific moment (server-client interaction). – The value of a variable reflects the “physical” state of the device. i.e: the bulb is on or off Conversational state example Assuming that a lamp is modelled by this transition system: • If the lamp is in state A • a client can invoke both turnOn and turnOff operation • But if the lamp is in state B • a client can only invoke the turn on operation turnOn turnOff A B turnOn A working example: DPWSLight DPWSLight service Home automation bus DPWSClient (e.g. Orchestrator) The behaviour model <?xml version="1.0" encoding="UTF-8"?> <service … <ts> <state name="B" type="initial-final"> <transition action="turnOn"> <target state="A" /> <postcondition variable="light"> <set-value value="0" /> </postcondition> </transition> </state> <state name="A" type="initial-final"> <transition action="turnOff"> <target state="B" /> <postcondition variable="light"> <set-value value="0" /> </postcondition> </transition> </state> </ts> </service> turnOff / light:=0 A B turnOn / ligth:=1 Creating states and variables • Remember: states and variables can assume only a finite set of values in order to make services’s composition feasible. • By looking at vdd/vml and sbl specifications files we see that allowed states are {A,B} while allowed values for light variable are {0,1}. • Then we create two classes in charge of limiting the possible values: – one to represent the states – and the other to represent the variable Variables and states declaration light_level.vml.xml light-variable.vdd.xml <?xml version="1.0" encoding="UTF-8"?> <variable-model ... name="LightLevel"> <?xml version="1.0" encoding="UTF-8"?> <home name="MyHome" … <variable name="light" type="custom" model="light_level.vml.xml" /> </home> <value value="0" id="0"/> <value value="1" id="1"/> </variable-model> <service … <ts> <state name="B" type="initial-final"> … </state> <state name="A" type="initial-final"> … </state> </ts> </service> light_type.sbl.xml State Class package common; public class State { public final static String STATE_A = "A"; public final static String STATE_B = "B"; private String state; /** * Create a State object and set its value to "off" */ public State(){ this.state = STATE_B; } public String getState(){ return state; } public void setStateA(){ this.state = STATE_A; } public void setStateB(){ this.state = STATE_B; } } LightDevice class • Then we create a class containing the “business logic” of the lamp. • Such class has: – A state attribute, a light variable (and the related accessor methods) – a method for each action declared (turnOn and turnOff) in the sbl. • Note that this class is the only one aware of the underlying specific home automation protocol. It sends commands to the bus and listens for event coming from the bus. Implementing service behaviour • Each method returns a boolean value according to the sbl specification: – If the current state of the lamp is “B” • If on() method is invoked than we turn on the lamp and return true • If off() method is invoked than we return false, saying to our client that is not possible to invoke such action in the current conversational state turnOff / light:=0 A B turnOn / ligth:=1 On method /** * Turns on the light */ public boolean on(){ boolean result = false; if (state.getState().equals(State.STATE_B)){ /* * Here the developer inserts protocol dependent instructions * to actuate the light. */ EDSbus.sendBMCOutputControl(deviceNumber, sendingNumber, outputChannel, true); result = true; } return result; } LightDevice class LightDevice • • • • boolean on() boolean off() String getState() int getVariable() Home automation bus 1..1 State 1..1 Variable Adding web service capabilities to our lamp • So far we developed a few classes able to communicate with the bus, in particular the LightDevice class. 1..1 State LightDevice 1..1 Variable • Now we will make it accessible through web services. Adding web service capabilities to our lamp 1..1 1..* Device 1..1 1..* Service Operation 1..1 1..1 1..1 DPWSLight LightService 1..1 OnOperation OffOperation GetStateOperation 1..1 1..1 1..1 GetValueOperation State 1..1 LightDevice 1..1 Variable GetSarOperation DPWSLight class public class DPWSLight extends DefaultDevice implements StateChangedListener public DPWSLight(LightDevice light) { super(); … this.light = light; service = new LightService(); OnOperation onOp = new OnOperation(light); OffOperation offOp = new OffOperation(light); GetStateOperation getStateOp = new GetStateOperation(light); GetValueOperation getValueOp = new GetValueOperation(light); GetServiceArchiveOperation getSarOp = new GetServiceArchiveOperation(); service.addOperation(onOp); service.addOperation(offOp); service.addOperation(getStateOp); service.addOperation(getValueOp); service.addOperation(getSarOp); … this.addService(service); OnOperation public class OnOperation extends Operation{ private LightDevice light; private static String retvalue = "result"; private static String ns = "ns”; Exactly the name of the action declared in the sbl public OnOperation(LightDevice lightdev){ super("turnOn",new QName("BasicService", ns)); light = lightdev; Element result = new Element(new QName(retvalue, ns), SchemaUtil.getSchemaType(SchemaUtil.TYPE_STRING)); this.setOutput(result); } @Override public ParameterValue invoke(ParameterValue pv) throws InvocationException { System.out.println("Light is on!"); boolean ret = light.on(); ParameterValue result = createOutputValue(); if (ret){ ParameterUtil.setString(result, "reply","true"); } else{ ParameterUtil.setString(result, "reply","false"); } return result; } Call to the on method of LightDevice (protocol-specific) The service archive • Thanks to the WSDL of the service, the client can make syntactically correct invocations of the operations • A client of our lamp service can retrieve its rich description by invoking a specific WS operation: GetServiceArchive. • The latter return a zip file containing – The variable type declaration (light_level.vml.xml) – The variable instance declaration (light-variable.vdd.xml) – The behaviour description (light_type.sbl.xml) • At this point the client knows the behaviour of a lamp and can make semantically correct invocations of the operations. – For example it knows that if the service is in conversational state B then it should not invoke the turnOff operation – and if it invokes the turnOn operation the value of the light variable will be 1 (it knows how an operation affects the environment) GetServiceArchieveOperation public class GetServiceArchiveOperation extends Operation{ private static String retvalue = "result"; private static String ns = "ns" ; public GetServiceArchiveOperation(){ super("getServiceArchive",new QName("BasicService", ns)); ComplexType rqComplex = new ComplexType(new QName("AttachmentType", ns), ComplexType.CONTAINER_SEQUENCE); rqComplex.addElement(new Element(new QName("File", ns), SchemaUtil.getSchemaType(SchemaUtil.TYPE_BASE64_BINARY))); Element messageOUT = new Element(new QName("AttachmentMsg", ns)); messageOUT.setType(rqComplex); this.setOutput(messageOUT); } public ParameterValue invoke(ParameterValue pv) throws InvocationException { System.out.println("Get service archive invoked!!!"); ParameterValue result = createOutputValue(); try { ParameterUtil.setAttachment(result, "File", DPWSFramework.getAttachmentFactory().createFileAttachment("resources/service_archive/BedroomLight.zip", "application/zip")); } catch (IOException e) { System.out.println(e.getMessage()); } return result; } } An Attachment is binary data which is attached to a SOAP message. The attachment isn’t transmitted inline the SOAP part of the message, but appended to the SOAP message by the use of the MIME format. Client-server interaction Client DPWSLight “Hello” syntax GetServiceArchive syntax + semanthics Service archive (zip file) GetState State = A turnOff Asynchronous events • So far we have seen only synchronous operations to retrieve the state/variable of our lamp. – A client can invoke the turnOn operation and, when the operation returns, it can check the conversational state by invoking the GetState operation • What happens if a user in the house presses a wall mounted light switch controlling the lamp? – The service must be aware of the event and change its internal representation of the device Asynchronous events public class LightDevice implements BusIndicationListener{ … @Override public void onIndication(BusEventIndication bei) { logger.info(bei.toString()); if(bei.getValue()==100){ variable.setValueOn(); state.setStateA(); notifyState(); notifyValue(); } else{ if(bei.getValue()==0){ variable.setValueOff(); state.setStateB(); notifyState(); notifyValue(); } } } … private void notifyValue(){ Iterator<VariableChangedListener> it = valueListeners.iterator(); logger.info("Notifying the change of value to all the listeners"); while (it.hasNext()){ VariableChangedListener currentListener = it.next(); logger.fine("Notifying to listener: " + currentListener.getClass().getName()); currentListener.VariableChanged(variable); } } Asynchronous events DefaultEventSource 1..1 1..1 DPWSLight 1..1 LightService 1..1 State LightDevice 1..1 Variable SimpleValueNotifica tion Client (subscribed to the event source) Clients • Thanks to DPWS libraries you can develop a custom client able to track the service dynamically, invoke operations and detect events. • But now we will see a generic and universal DPWS client in action… Device Service PortTypes DPWS Explorer Endpoint reference Operations http://ws4d.e-technik.uni-rostock.de/dpws-explorer/