Infairy Cocina SDK(2013/07/03)

索引
1. 環境準備
2. 第一支 bundle
3. Infairy Cocina用到的bundles
4. 如何註冊一個 device 到 Infairy Cocina
5. 如何控制 3rd Party 所開發的 devices.
6. 如何監聽事件(event).
7. 如何監聽語音辨識事件.
8. 如何在平板上測試bundle.
Top

  1. 環境準備
  2. 別忘了,如果有編譯時期的錯誤,請一下列方式設定Eclipse:
    * Go to Window-->Preferences-->Java-->Compiler-->Error/Warnings.
    * Select Deprecated and Restricted API. Change it to warning.
    * Change forbidden and Discouraged Reference and change it to warning. (or as your need.)
    Top

  3. 第一支 bundle
  4. 一旦你完成上面環境的準備工作,你已經可以開始撰寫第一個bundle,
    請依照我們為你準備的第一個 bundle – Hello Infairy! 一步步來完成練習。

    1. 首先,新建Package
      1. 建立java package

      2. 輸入 package 名稱: com.Acompany.first.hello

      3. 你可以看到你所新建的Package名稱,會出現在這裡:

    2. 新建一個class
      1. Class 名稱: Hello

      2. 輸入class 名稱: Hello
    3. 開始撰寫程式碼
      1. 程式碼名稱: Hello.java
      2.                     
                            package com.Acompany.first.hello;
                    
                            import org.osgi.framework.BundleActivator;
                            import org.osgi.framework.BundleContext;
                            
                            public class Hello implements BundleActivator{
                            
                                public void start(BundleContext arg0) throws Exception {
                                    System.out.println("Hello, Infairy!!");
                                }
                            
                                public void stop(BundleContext arg0) throws Exception {
                                    
                                }
                            }
                    
                            
                        
    4. 編譯 (Make bundle)
      1. 建立一個"BND"目錄


      2. 新增一個bnd 檔, 檔名: com.Acompany.first.hello.bnd


      3. 撰寫編譯檔( bnd ):
      4.                 	
                                    Export-Package: com.Acompany.first.hello  
                                    Bundle-ClassPath: .
                                    Bundle-Name: A Company Hello Infairy.
                                    Bundle-Vendor: A Company Tech.
                                    Bundle-Version: 1.0.0
                                    Bundle-Activator: com.Acompany.first.hello.Hello
                                    Bundle-Description: This is a Infairy Cocina bundle file.
                                    Bundle-SymbolicName: com.Acompany.first.hello
                                    Import-Package: org.osgi.framework;version=!
        					
                        
      5. 執行編譯(Make Bundle)

      6. 編譯成功,產生Bundle (.jar檔),

    5. 執行
      1. 執行 felix framework (參考 http://felix.apache.org/site/integrating-felix-with-eclipse.html)
      2. 安裝Bundle

      3. 啟動 Bundle

      恭喜!! 你已經完成第一支bundle!

    Top

  5. Infairy Cocina用到的bundles
  6. Infairy Cocina 有5個基本的bundle, 都是由Java所開發的, 這些bundle可以提供開發者開發專屬的bundle,而且不需要任何Z-Wave的專業控制知識就可以開發智能家居的設備。
    系統開發者可以透過這些bundle完成下列功能:
    • 取得Infairy Cocina智能家居平版內,用戶所安裝的設備列表
    • 監控平版內所有設備的狀態,包含偵測器的的觸發訊息
    • 加入Infairy Cocina平版內,並提供其他開發者操控。
    • 操控其他開發者的Bundle.
    詳細的開發文件請參考: SDK Document.
    以下是5個基本Bundle的列表:
    1. com.infairy.smarthome.bundle.property.jar
      用途: Infairy Cocina環境變數服務。
    2. com.infairy.smarthome.bundle.device.jar
      用途: 提供各個Bundle間的設備操控命令,可以直接透過此Bundle操控Z-Wave設備,也可以讓開發者操控其他開發者的功能。
    3. com.infairy.smarthome.bundle.eventCondition.jar
      用途: 提供各個device的事件處發程序,並處理連動命令。
    4. com.infairy.smarthome.bundle.runCondition.jar
      用途:執行連動命令。
    5. com.infairy.smarthome.bundle.tools.jar
      用途:一般系統開發程序集。
    Top

  7. 如何註冊一個 device 到 Infairy Cocina
    1. Infairy Cocina可以讓開發者自行開發程式讓Infairy Cocina的其他Bundle可以叫用!以下是一個完整的程式,提供三個功能:

      1. 報時: 當執行報時功能時,會將目前的時間組成一段話,利用系統提供的addTTS()函數將時間字串填入,Infairy@Andorid就會用語音念出。此功能一旦被設定在Infairy Cocina的排程中依據指定的時間執行,就可以作為報時的功能。
      2. 說笑話: 此功能會擷取Kingnet國家網路醫院所提供的笑話庫,擷取一段笑話,同樣的利用系統提供的addTTS()函數,則可以在Infairy Cocina裡面任何時機點讓系統說笑話,例如:當門口警報器觸發時可以講笑話、也可以指定每天早上六點講笑話作為鬧鐘功能。
      3. 特定笑話: 此功能與[ii.說笑話]功能相同,但是可以讓用戶指定笑話關鍵字,展示的昰如何將具有參數的函數註冊到Infairy Cocina.

    
    
            package com.infairy.APP.TTS;
            
            import java.io.ByteArrayOutputStream;
            import java.io.InputStream;
            import java.net.HttpURLConnection;
            import java.net.URL;
            import java.net.URLDecoder;
            import java.net.URLEncoder;
            import java.util.Dictionary;
            import java.util.Hashtable;
            import java.util.Properties;
            import java.util.Vector;
            
            import org.osgi.framework.BundleActivator;
            import org.osgi.framework.BundleContext;
            import org.osgi.framework.ServiceReference;
            import org.osgi.framework.ServiceRegistration;
            import org.osgi.service.event.Event;
            import org.osgi.service.event.EventConstants;
            import org.osgi.service.event.EventHandler;
            
            import com.infairy.smarthome.bundle.device.DevicePool;
            import com.infairy.smarthome.bundle.property.Property;
            import com.infairy.smarthome.tools.Tools;
            
            
            public class speech  implements BundleActivator,EventHandler{
                DevicePool device=null;
                Property property;
                
                Dictionary dict = new Hashtable();
                
                BundleContext context;
            
                String ZID="";
                ServiceRegistration register;
                
                
                final static String[] topic = { "com/infairy/TTS"};  //聆聽語音識別的屬性
                
                public void start(BundleContext context) throws Exception {
                    this.context=context;
                    //取得Property服務
                    property=(Property)getService(Property.class.getName(), "(Property=Setting)");
                    
                    //取得device服務
                    device=(DevicePool)getService(DevicePool.class.getName(), property.getDeviceService());
                    
                    /* 註冊本bundle成為一個Infairy家族成員*/
                    device.registerDevice(context, this);
            
                    
                    /* 設定要註冊的函數型式 
                    *  格式:
                    *       new Object[]{函數別名(供使用者識別用),  真正函數名稱, 傳入的參數別名陣列}, 參數的數值內容陣列
                    *  例如:
                    *      本範例有一個 AssignJoke(String key) 函數
                    *      那註冊的函數就必須是 new Object[]{"特定笑話","AssignJoke",  new String[]{"關鍵字"}, new String[]{""}}
                    *    
                    *      若函數的參數是 int , 例如  checkItem(String no, int value)
                    *      那註冊的函數就必須是 new Object[]{"檢查項目","checkItem",  new String[]{"編號", "值"} , new String[]{"",""}}
                    *
                    *      請注意, 註冊函數不需要管該函數的型態, 但是個數必須相同!
                    *
                    *  參數的數值內容陣列格式:
                    *       Infairy可以讓開發者指定傳入參數的內容, 格式說明如下:
                    *
                    *        new String[]{"關鍵字"}, new String[]{""}  , 關鍵字內容為 空字串
                    *        new String[]{"關鍵字"}, new String[]{"電腦"}, 關鍵字內容為  電腦
                    *        new String[]{"關鍵字"}, new String[]{"@電腦|體育|吃飯@"} , 關鍵字內容 為選項{電腦 / 體育 / 吃飯}
                    *        new String[]{"關鍵字"}, new String[]{"#Alias#"} 關鍵字內容 係平版內安裝所有設備的別名, 
                    *                     但是請注意, 平版安裝的設備必須與本元件[ex. 語音達人] 的 GKIND (ex. speech)相同才會顯示
                    *                     也就是說, 當被控設備在新增時, 就必須輸入相對應的[廠商代碼](ex. speech)才能被控制
                    *                     廠商代碼應該在廠商設備的說明書, 或是本元件安裝的說明文字中標示, 以利識別。
                    */
                    Vector functions=new Vector();
                    functions.addElement(new Object[]{"報時",    "reportTime", null, null});
                    functions.addElement(new Object[]{"說笑話",	"justJoke", null, null});
                    functions.addElement(new Object[]{"特定笑話","AssignJoke",  new String[]{"關鍵字"}, new String[]{""}});
                    
                    //設定註冊函數
                    dict.put("Function", functions);
                    
                    /*
                    *  指定哪幾個特定功能要整合進SDK中, DevicePool 裡的ON / OFF / USER 操作
                    *   ex.
                    *        DevicePoole device=(DevicePool)getService(..);
                    *        device.ON("comInfairyTTSspeech");   //會執行 reportTime
                    *        device.OFF("comInfairyTTSspeech");  //會執行  justJoke    
                    *        device.USER("comInfairyTTSspeech", new String[]{"笑話關鍵字"); //會執行  AssignJoke, 並傳入 "笑話關鍵字"
                    *
                    *   請注意:
                    *       ON / OFF 指定的程序, 均不能有參數傳遞
                    */   
                       
                   	Vector Operation=new Vector();
    				Operation.addElement(new Object[]{"Device.ON", "reportTime"});  //指定 reportTime 入 device ON() 操作
    				Operation.addElement(new Object[]{"Device.OFF", "justJoke"});   //指定 reportTime 入 device OFF() 操作
    				Operation.addElement(new Object[]{"Device.SET", "AssignJoke"}); //指定 reportTime 入 device USER() 操作
                    
                    //設定註冊的操作程序
    				dict.put("Operation", Operation);  
                    
                    //設定註冊的Class
                    dict.put("Class", this.getClass());
                    
                    //設定此Bundle的別名供使用者識別
                    dict.put("Alias","語音達人");
                    
                    //自訂類別,可以讓開發者識別的類別字串
                    dict.put("GKIND", "Speech");
                    
                    /*
                       將此Bundle以device型態加入到Infairy Cocina
                    */
                    ZID=device.addDevice(dict);
                    
                 	/*
                     * 加入語音識別廣播聆聽程序
                     * 
                     */
                    Dictionary dict = new Hashtable();
                    dict.put(EventConstants.EVENT_TOPIC, topic);
                    register = context.registerService(EventHandler.class.getName(), this,	dict);
    
                }
                
                public void stop(BundleContext arg0) throws Exception {
                    if(!ZID.equals(""))
                        device.removeDevice(ZID);
                }
            
            
                //報時
                public void reportTime(){
                    Property property=(Property)getService(Property.class.getName(), "(Property=Setting)");
                    String dt=Tools.getSystemTime(property.TimeZoneID);
                    String tm="";
                    if(dt.substring(10,12).equals("00") || dt.substring(10,12).equals("0"))
                        tm="現在時間"+dt.substring(8,10)+"點整";
                    else
                        tm="現在時間"+dt.substring(8,10)+"點"+dt.substring(10,12)+"分";
                        
                    Tools.addTTS(tm);  //請參考SDK
                }
                
                //特定笑話
                public void AssignJoke(String K){
                    talkJoke(K);
                }
                
                //說笑話
                public void justJoke(){
                    talkJoke("");
                }
                
    			//擷取網路笑話            
                private void talkJoke(String K){
                	
                    //Kingnet國家網路醫院笑話網址, 由於授權因素,故隱藏實際笑話網址
                    String urlString="http://xxxxxxx.xxxx.xxx/JokelLocation.html";
                    
                    if(K.equals(""))
                            urlString+="?K="+K;
                    
                    //開始擷取http url 內容
                    try{
                        URL url = new URL(urlString);  
                        HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                        conn.connect();   
                        InputStream is = conn.getInputStream();
                        ByteArrayOutputStream bStrm;
                        bStrm= new ByteArrayOutputStream();
                
                        int ic;
                        while (true){
                            byte[] kk=new byte[1024];
                            ic = is.read(kk);
                            if (ic==-1){
                                if (bStrm != null){
                                    byte[] bbb=bStrm.toByteArray();
                                    try{
                                        String YYY=new String(bbb, "UTF-8");
                                        Tools.addTTS(removeHTMLSymbol(YYY));
                                    }catch(Exception e){
                                        System.out.println("err-2:"+e.getMessage());
                                    }
                                }
                                break;
                            }else{
                                for (int k0=0; k0 < ic; k0++){                           
                                    int chkIC=(int)kk[k0];
                                    if (chkIC<0) chkIC+=256;                                
                                    bStrm.write(chkIC);
                                }
                            }
                        }
                        bStrm.reset();
                        bStrm.close();
                        bStrm=null;
                        is.close();
                        is=null;
                        conn=null;
                        url=null;
                    }catch(Exception e){
                        System.out.println("err-1:"+e.getMessage());
                        
                    }        
                    
                }
                
                //移除網址內的特定符號,以避免TTS讀取的障礙
                private String removeHTMLSymbol(String s0){
                    String s=s0;
                    int a=s.indexOf("&");
                    int b=s.indexOf(";", a+1);
                    if(a==-1 || b==-1) return s;
                    
                    while(true){
                        s=s.substring(0,a)+s.substring(b+1);
                        a=s.indexOf("&");
                        b=s.indexOf(";", a+1);
                        if(a==-1 || b==-1) break;
                    }
                    
                    return s;
                }
                
                  
                //註冊服務程序
                private Object getService(String className, String property){
                    Object obj=null;
                    try{
                        System.out.println("c="+className+", p="+property);
                        ServiceReference[] ref = context.getServiceReferences(className, property);
                        if (ref != null){
                                obj=context.getService(ref[0]);
                        }
                    }catch(Exception e){
                        System.out.println("("+className+")Couldn't find Service..."+e.getMessage());
                    }
                    return obj;
                }
                
                /*
                *
                *
                *   任何語音識別廣播會透過下面程序收到廣播的文字及內容
                *	參數說明:
                *     BroadcastID:    指定廣播對象bid
                *     SelectedIndex:  讀取用戶點選的按鈕索引值
                *     VRContent:      語音識別的文字內容
                *     VR_NUMBER:      語音識別中出現的數值
                *     VR_LATLNG:      目前用戶平版所在的經緯度座標
                *     VR_HOUR:        語音識別中出現的時間-小時
                *     VR_MIN:         語音識別中出現的時間-分
                */
                public void handleEvent(Event evnt) {
                	String bid=(String) evnt.getProperty("BroadcastID");
    				if(!bid.equals("") && !bid.equals(ZID)) //若是有指定廣播對象bid,而且不是自己的ZID, 就不要處理
    					return;
    				
                    String index=(String) evnt.getProperty("SelectedIndex"); //讀取用戶點選的按鈕索引值(此範例沒有用到)
    				String VR[]=(String[]) evnt.getProperty("VRContent");  //讀取語音識別的文字內容
    				String VR_NUM=(String) evnt.getProperty("VR_NUMBER");  //讀取語音識別中出現的數值(此範例沒有用到)
    				String LATLNG=(String) evnt.getProperty("VR_LATLNG");  //讀取目前用戶平版所在的經緯度座標(此範例沒有用到)
    				String HOUR=(String) evnt.getProperty("VR_HOUR");  //讀取語音識別中出現的時間-小時(此範例沒有用到)
    				String MIN=(String) evnt.getProperty("VR_MIN");  //讀取語音識別中出現的時間-分(此範例沒有用到)
    
    				for(int i=0; i < VR.length; i++){
    					if(VR[i].indexOf("笑話")!=-1){
    						int n=VR[i].indexOf("關於");
    						int n1=VR[i].indexOf("笑話");
    						if(n==-1){
    							justJoke("我是語音達人,我來幫你講一個笑話");
    						}else{
    							int m=VR[i].indexOf("的笑話");
    							if(m==-1){
    								//講一個笑話,關於xxx
    								String key=VR[i].substring(n+"關於".length());
    								AssignJoke("我是語音達人,我來幫你講一個關於"+key+"的笑話", key);
    							}else{
    								if(n < m){  //關於XXX的笑話
    									String key=VR[i].substring(n+"關於".length(), m);
    									AssignJoke("我是語音達人,我來幫你講一個關於"+key+"的笑話",key);
    								}else{
    									if(n < n1){
    										// 講一個笑話,關於xxx
    										String key=VR[i].substring(n+"關於".length());
    										AssignJoke("我是語音達人,我來幫你講一個關於"+key+"的笑話",key);
    									}else{ //講一個關於xxxx笑話
    										String key=VR[i].substring(n+"關於".length(), n1);
    										AssignJoke("我是語音達人,我來幫你講一個關於"+key+"的笑話",key);
    									}
    								}
    							}
    						}
    						break;
    					}else if(VR[i].indexOf("報時") != -1){
    						reportTime();
    						break;
    					}
    				}
    			}
    		}
    
    		
    
    當您所撰寫的程式碼完成後,接下來就是放入 iStore :
    請先申請 Infairy Store 的帳號及密碼後,進入 Infairy Store (iStore) 來上傳您的Bundle

    上傳完成後,您即可在iStore內看到您的 bundle

    執行[Install]按鈕

    安裝完成後,使用者可以在[連動設定]/[定時排程]/[情境]等功能中看到您的功能設定:
    [連動設定]

    [定時排程]

    [情境]

Top

  • 如何控制 3rd Party 所開發的 devices.
  • Top

  • 如何監聽事件(event).
  • Top

  • 如何監聽語音辨識事件.
  • Top

  • 如何在平板上測試bundle.
    1. Bundle register parameter Bundle-Index for API get bundle properties purpose Bundle-Function Bundle-Alias Function : for SDK get service purpose