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
- 環境準備
-
請從此處下載 Eclipse:http://www.eclipse.org/downloads/
-
請從此處下載 Apache Felix:http://felix.apache.org/site/downloads.cgi
-
整合Felix與Eclipse,請參考: http://felix.apache.org/site/integrating-felix-with-eclipse.html
-
請從此處下載 BND jar file: http://www.java2s.com/Code/Jar/b/Downloadbnd00384jar.htm
-
複製 BND jar 檔到 Eclispe的plugins目錄.
-
複製上面Infairy Cocina用到的bundle到 Eclipse 的 workspace目錄, 例如: /Felix/bundle:
- com.infairy.smarthome.bundle.property.jar
- com.infairy.smarthome.bundle.device.jar
- com.infairy.smarthome.bundle.eventCondition.jar
- com.infairy.smarthome.bundle.runCondition.jar
- com.infairy.smarthome.tools.jar
再將上述bundle加到Eclipse 的 Java Build Path: -
最後,設定Java的編譯及執行環境.
- 第一支 bundle
- 首先,新建Package
- 建立java package
- 輸入 package 名稱: com.Acompany.first.hello
- 你可以看到你所新建的Package名稱,會出現在這裡:
- 新建一個class
- Class 名稱: Hello
- 輸入class 名稱: Hello
- 開始撰寫程式碼
- 程式碼名稱: Hello.java
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 { } }
- 編譯 (Make bundle)
- 建立一個"BND"目錄
- 新增一個bnd 檔, 檔名: com.Acompany.first.hello.bnd
- 撰寫編譯檔( bnd ):
- 執行編譯(Make Bundle)
- 編譯成功,產生Bundle (.jar檔),
- 執行
- 執行 felix framework (參考 http://felix.apache.org/site/integrating-felix-with-eclipse.html)
- 安裝Bundle
- 啟動 Bundle
- Infairy Cocina用到的bundles Infairy Cocina 有5個基本的bundle, 都是由Java所開發的, 這些bundle可以提供開發者開發專屬的bundle,而且不需要任何Z-Wave的專業控制知識就可以開發智能家居的設備。
- 取得Infairy Cocina智能家居平版內,用戶所安裝的設備列表
- 監控平版內所有設備的狀態,包含偵測器的的觸發訊息
- 加入Infairy Cocina平版內,並提供其他開發者操控。
- 操控其他開發者的Bundle.
- com.infairy.smarthome.bundle.property.jar
用途: Infairy Cocina環境變數服務。 - com.infairy.smarthome.bundle.device.jar
用途: 提供各個Bundle間的設備操控命令,可以直接透過此Bundle操控Z-Wave設備,也可以讓開發者操控其他開發者的功能。 - com.infairy.smarthome.bundle.eventCondition.jar
用途: 提供各個device的事件處發程序,並處理連動命令。 - com.infairy.smarthome.bundle.runCondition.jar
用途:執行連動命令。 - com.infairy.smarthome.bundle.tools.jar
用途:一般系統開發程序集。 - 如何註冊一個 device 到 Infairy Cocina
- 報時: 當執行報時功能時,會將目前的時間組成一段話,利用系統提供的addTTS()函數將時間字串填入,Infairy@Andorid就會用語音念出。此功能一旦被設定在Infairy Cocina的排程中依據指定的時間執行,就可以作為報時的功能。
- 說笑話: 此功能會擷取Kingnet國家網路醫院所提供的笑話庫,擷取一段笑話,同樣的利用系統提供的addTTS()函數,則可以在Infairy Cocina裡面任何時機點讓系統說笑話,例如:當門口警報器觸發時可以講笑話、也可以指定每天早上六點講笑話作為鬧鐘功能。
- 特定笑話: 此功能與[ii.說笑話]功能相同,但是可以讓用戶指定笑話關鍵字,展示的昰如何將具有參數的函數註冊到Infairy Cocina.
* 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
一旦你完成上面環境的準備工作,你已經可以開始撰寫第一個bundle,
請依照我們為你準備的第一個 bundle – Hello Infairy! 一步步來完成練習。




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=!




Top
系統開發者可以透過這些bundle完成下列功能:
以下是5個基本Bundle的列表:
Top
Infairy Cocina可以讓開發者自行開發程式讓Infairy Cocina的其他Bundle可以叫用!以下是一個完整的程式,提供三個功能:
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
Top
Top
Top
-
Bundle register parameter
Bundle-Index for API get bundle properties purpose
Bundle-Function
Bundle-Alias
Function : for SDK get service purpose