2014年4月21日 星期一

IMP control APK development

1. APK UI 解說

   1.1 開啟畫面  
開起畫面如上圖貼, 並伴隨音樂  .中間雲端圖示是"借用" IMP 網站的.
帳號功能尚未加入, 輸入密碼後才能進入操作畫面

  1.2 操作畫面 2-1
一進入操作畫面先執行狀態檢查.....


 1.2 操作畫面 2-2
檢查完畢若雙端網路皆開啟 (手機&IMP)且IMP 與server 連線正常,狀態列顯示"狀態已更新", 並自動調整開關位置反應實際狀況, 下方黑色顯示區 1,1,1,1,1,1 為IMP 輸出準位 (1=H, 0=L).


1.3 錯誤畫面
檢查完畢若IMP無正常連線 pop up a "無線網卡無回應" message


2.程式碼架構


3.1 MainActivity.java


相關變數宣告及定義
============================================
public class MainActivity extends Activity {
/** Called when the activity is first created. */
 TextView etResponse;  // 顯示密碼登入狀況
 private Button bt1;   // "進入" 按鍵
 String UID="jason";   // 密碼
 private int alertId;  
 private SoundPool soundPool;
 Handler aHandler;



聲音播放  
==================================================
    soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 5);
    alertId = soundPool.load(this, R.raw.start_up, 1); 

    aHandler = new Handler();
    aHandler.post(runnable); 

 final Runnable runnable = new Runnable() {
        public void run() {
          try{
          Thread.sleep(500);  
         }
    catch(InterruptedException e){
           e.printStackTrace();
          }
      soundPool.play(alertId, 1.0F, 1.0F, 0, 0, 1.0F); 
 }
          };


第一行, 將soundPool產生實體,第一個參數為soundPool可以支持的聲音數量,這決定了Android為其開設多大的緩衝區,第二個參數為聲音類型,在這裡標識為系統聲音,除此之外還有AudioManager.STREAM_RING以及AudioManager.STREAM_MUSIC等,系統會根據不同的聲音為其標誌不同的優先順序和緩衝區,最後參數為聲音品質,品質越高,聲音效果越好,但耗費更多的系統資源。

第二行,系統為soundPool載入聲音,第一個參數為上下文參數,第二個參數為聲音的id,一般我們將聲音資訊保存在resraw資料夾下。第三個參數為聲音的優先順序,當多個聲音衝突而無法同時播放時,系統會優先播放優先順序高的。

為了解決  SoundPool 音效直接放在onCreate裡,卻放不出來  (原來問題出在於播放音效時,音效檔尚未加載完成所以在DDMS上會出現sample not ready的訊息,沒有完成載入音效檔導致不能播放。). 使用timer物件runnable方法來給程式時間. 這裡延遲了 500 msec.


這裡利用 Intent 來執行 Activity 的切換 (跳至 LEDActivity, 沒有傳任何值)
==============================================================
public void onClick(View v) {
 
   String content = CodeEditText.getText().toString(); //gets you the contents of edit text
 
         if (content.equals(UID))  {  
        etResponse.setBackgroundColor(0xFF00DD00);
        etResponse.setText(content+"...ok");   
            Intent intent = new Intent();
            intent.setClass(MainActivity.this, LEDActivity.class);
            startActivity(intent);
       }          
         else{
         etResponse.setBackgroundColor(0xFFFF5809);
         etResponse.setText("密碼錯誤");   
                      }
          }


4. LEDActivity

相關變數宣告及定義
============================================
public class LEDActivity<var> extends Activity {

    TextView etResponse;     
    TextView tvIsConnected;  // 狀態列訊息
    private Button bt1;      // 狀態確認鍵
    private Switch sw1,sw2,sw3,sw4,sw5,sw6;  // 開關鍵
    String disconnect= "手機網路不存在";    // 手機網路未開錯誤訊息
    String checkIMP="請檢查無線網卡";   //未能連接 IMP 錯誤訊息
    String one="1";
    String UID="jason";           // 網址加入 UID 與 IMP squirrel agent code 設定一致 
        
    private Button t_bt1_on,t_bt2_on,t_bt3_on,t_bt4_on,t_bt5_on,t_bt6_on;    // 定開按鍵
    private Button t_bt1_off,t_bt2_off,t_bt3_off,t_bt4_off,t_bt5_off,t_bt6_off;  // 定關按鍵
    int timer=10000;    // 定該關時間 10sec
    int setting=0;
    private MyCount mc;  //計時器宣告
    String LED="";
    int vibration=0;    // vibrator 震動持續時間
    private int alert_button, alert_error; // 聲音檔 ID 定義
    private SoundPool soundPool;


計時器代碼
============================================
class MyCount extends CountDownTimer {   
public MyCount(long millisInFuture, long countDownInterval) {   
super(millisInFuture, countDownInterval);   
}   
@Override   
public void onFinish() 
{   
}   
@Override   
public void onTick(long millisUntilFinished) {                    
          tvIsConnected.setText(LED+"設定時間:"+timer/1000+" /剩餘時間:"+millisUntilFinished/1000);
             }
    }   


OnCreate代碼
============================================
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.led_main);

        // get reference to the views
        etResponse = (TextView) findViewById(R.id.etResponse);
        tvIsConnected = (TextView) findViewById(R.id.tvIsConnected);
       
        soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 5);
        alert_button = soundPool.load(this, R.raw.button, 1); 
        alert_error = soundPool.load(this, R.raw.error, 1);
        
        // add for timer 0411
        RegisterAlarmBroadcast();
        
        tvIsConnected.setText("狀態檢查中...");
        new StatusAsyncTask().execute("https://agent.electricimp.com/nGZM1At9qnAa?uid="+UID+"&status");
          
        bt1 = (Button) findViewById(R.id.button1);
        bt1.setOnClickListener(new Button.OnClickListener() {
              // check if bt1 pressed
        public void onClick(View v) {
         
        vibrate(vibration);
         
        soundPool.play(alert_button, 1.0F, 1.0F, 0, 0, 1.0F);
         
        // check if network is connected
                if(isConnected())
                        {  
                   tvIsConnected.setText("狀態檢查中...");
                         new StatusAsyncTask().execute("https://agent.electricimp.com/nGZM1At9qnAa?                                         uid="+UID+"&status");
                          }                                       
                  // not connected               
                 else{
                   // tvIsConnected.setBackgroundColor(0xFFFF5809);
                        // tvIsConnected.setText(disconnect); 
                soundPool.play(alert_error, 1.0F, 1.0F, 0, 0, 1.0F);
                         Builder dialog = new AlertDialog.Builder(LEDActivity.this)
                         .setIcon(android.R.drawable.btn_star_big_on)
                         .setTitle("錯誤")  
                         .setMessage(disconnect)
                         .setNegativeButton("close",null);
                         dialog.show();             
                      }
              }
     });
        
        sw1 = (Switch) findViewById(R.id.switch1); 
        sw1.setOnCheckedChangeListener(new OnCheckedChangeListener() {   
        public void onCheckedChanged(CompoundButton buttonView,
        boolean isChecked) {
            //   tvIsConnected.setBackgroundColor(0xFFFFFFFF);
        soundPool.play(alert_button, 1.0F, 1.0F, 0, 0, 1.0F);
           vibrate(vibration);
        if(isConnected()){ 
             if (isChecked)                
               // sw1 on
                             new HttpAsyncTask().execute("https://agent.electricimp.com/nGZM1At9qnAa?uid="+UID+"&dev1=1");                                                             
                   // sw1 off                
                       else          
                         new HttpAsyncTask().execute("https://agent.electricimp.com/nGZM1At9qnAa?uid="+UID+"&dev1=0");                          
                           }  
                 
                else {
                 // not connected
                     // tvIsConnected.setBackgroundColor(0xFFFF5809);
                    // tvIsConnected.setText(disconnect);
                soundPool.play(alert_error, 1.0F, 1.0F, 0, 0, 1.0F);
                    Builder  dialog = new AlertDialog.Builder(LEDActivity.this)
                    .setIcon(android.R.drawable.btn_star_big_on)
                    .setTitle("錯誤")  
                    .setMessage(disconnect)
                    .setNegativeButton("close", null);
                    dialog.show(); 
                     }      
           }
              }); 

進入操作畫面立即執行 StatusAsyncTask() 狀態檢查, 之後偵測 button and switch, 若是在有網路情況下則執行  HttpAsyncTask() 否則執行錯誤對話框 AlertDialog 顯示錯誤.


定時開關代碼
============================================
 t_bt1_on = (Button) findViewById(R.id.button_t1);
        t_bt1_on.setOnClickListener(new Button.OnClickListener() {
       
               public void onClick(View v) {
         
        alarmManager.set( AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timer , pendingIntent );
        setting=11;
        mc=new MyCount(timer,1000);
        mc.start();
        LED="LED1";
                      }
              });
              
        t_bt1_off = (Button) findViewById(R.id.button_t1off);
        t_bt1_off.setOnClickListener(new Button.OnClickListener() {      
       
        public void onClick(View v) 
           {
        alarmManager.set( AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timer , pendingIntent );
        setting=10;
        mc=new MyCount(timer,1000);
        mc.start();
        LED="LED1";
        }
              });        

t_bt1_on/off 為定是開關鍵, 按下時執行計數 MyCount , 計數完成 傳出 setting 常數供開關改變狀態


HTTP send and get 代碼
============================================

public static String GET(String url){
        InputStream inputStream = null;
        String result = "";
        try {
             
              HttpClient httpclient = new DefaultHttpClient();   
              HttpResponse httpResponse = httpclient.execute(new HttpGet(url));
              inputStream = httpResponse.getEntity().getContent();
              if(inputStream != null)
                  result = convertInputStreamToString(inputStream);
              else
                  result = "Did not work!";
             } catch (Exception e) {
                   Log.d("InputStream", e.getLocalizedMessage());
                 }
              return result;
          }
    
    private static String convertInputStreamToString(InputStream inputStream) throws IOException{
        BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(inputStream));
        String line = "";
        String result = "";
        while((line = bufferedReader.readLine()) != null)
            result += line;
           inputStream.close();
          return result;
         }

    public boolean isConnected(){
        ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
            if (networkInfo != null && networkInfo.isConnected()) 
                return true;
            else
                return false;   
            }

public class HttpAsyncTask extends AsyncTask<String, Void, String> {
              
        @Override 
        protected String doInBackground(String... urls) {
         
          GET(urls[0]); 
                 return GET(urls[0]);
               }
          
        // onPostExecute displays the results of the AsyncTask.
       @Override
        protected void onPostExecute(String result) {
         
            if(result.substring(0, 1).equals("I")) {
           
                   try {
 Thread.sleep(2000);
                catch (InterruptedException e)
                               {
e.printStackTrace();
}
       
              soundPool.play(alert_error, 1.0F, 1.0F, 0, 0, 1.0F);
           Builder  dialog = new AlertDialog.Builder(LEDActivity.this)
           .setIcon(android.R.drawable.btn_star_big_on)
                    .setTitle("錯誤")  
                    .setMessage("無線網卡無回應")
           .setNegativeButton("close", null);
                     dialog.show();
                tvIsConnected.setText(checkIMP);
               }
            
            else {
              tvIsConnected.setText("");
            Toast.makeText(getBaseContext(), result, Toast.LENGTH_LONG).show(); 
                 }
              }
           }

此處參考 http://hmkcode.com/android-internet-connection-using-http-get-httpclient/  文中所寫代碼, 唯其中 GET(urls[0]) 同時執行兩次 是為了克服 IMP agent 於回傳至 HTTP 時並非為IMP 及時狀態, 而是會有一次的慢差.


HTTP send and get 代碼
============================================
    private void RegisterAlarmBroadcast()
             {
             mReceiver = new BroadcastReceiver()
              {
                     @Override
             public void onReceive(Context context, Intent intent)
              {
                Toast.makeText(context, "設定完成!", Toast.LENGTH_LONG).show();
                tvIsConnected.setText("");
                switch(setting){
                    case 11: 
                       sw1.setChecked(true);
                       break;
                    case 10:    
                       sw1.setChecked(false);
                       break;
                    case 21: 
                       sw2.setChecked(true);
                       break;
                    case 20:    
                       sw2.setChecked(false);
                       break;                    
                    case 31: 
                       sw3.setChecked(true);
                       break;
                    case 30:    
                       sw3.setChecked(false);
                       break;
                    case 41: 
                       sw4.setChecked(true);
                       break;
                    case 40:    
                       sw4.setChecked(false);
                       break;    
                    case 51: 
                       sw5.setChecked(true);
                       break;
                    case 50:    
                       sw5.setChecked(false);
                       break;
                    case 61: 
                       sw6.setChecked(true);
                       break;
                    case 60:    
                       sw6.setChecked(false);
                       break;    
                        }            
                    }
                };

             // register the alarm broadcast here
              registerReceiver(mReceiver, new IntentFilter("com.techblogon.alarmexample") );
              pendingIntent = PendingIntent.getBroadcast( this, 0, new Intent("com.techblogon.alarmexample"),0          );
              alarmManager = (AlarmManager)(this.getSystemService( Context.ALARM_SERVICE ));
        }

接收計數器結束後 回傳的 setting 值 供開關狀態更新
參考 http://techblogon.com/simple-android-alarmmanager-example-to-schedule-an-event/



Vibrator 代碼
============================================

 public void vibrate(int duration)
    {
       Vibrator myVibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
       myVibrator.vibrate(duration);    
    }
    
標準寫法, 不多囉嗦了.




2014年3月5日 星期三

民宿紀要

1. 主管機關
    中央: 交通部觀光局                                             執掌: 法規
    地方: 台東縣政府--觀光旅遊處                          執掌: 管理
    地方: 台東縣政府--觀光旅遊處--觀光管理科   執掌: 檢查
              台東縣政府觀光旅遊處--Facebook           動態時報
 

2. 交通部觀光局登記網址
    363   鹿台花園山莊民宿

3.相關法規
     2.1  交通部觀光局 民宿管理辦法
     2.2  交通部觀光局 民宿法規 Q&A    
     2.3  臺東縣特色民宿審查作業要點
     2.4  臺東縣民宿登記暨審核流程圖
     2.5  臺東縣政府民宿聯合檢查執行要點
     2.6  臺東縣政府辦理熱氣球體驗活動收費標準
     2.7  臺東縣政府辦理熱氣球嘉年華入園車輛收費標準

4. 稅賦相關
     4.1  民宿業者須辦理稅籍登記嗎?


5. 相關認證
     5.1  合法民宿專用標識 
    


    5.2  好客民宿標章
    


   5.3  國民旅遊卡特約商店
          考核單位

 


 5.4  SGS 3S 認證



6. 相關網站
    6.2  台灣民宿協會
    6.3  中華民國滑雪草協會    

7. TBD

  

2014年2月26日 星期三

WEL-3000A 安裝

所需工具:電鑽(轉換頭, 起子,鑽頭, 16mm/50mm 圓穴鋸,磨砂鑽頭)
              榔頭
              鑿刀
              銼刀
              鋸子
所需材料:WEL-3000A; 杉木600mmx200mmx14mm
           
  電鑽  Durofix RI1265  3200NT   (2014/02/26 益昌購買, 俊緯強烈推薦, 130Nm 確實夠強悍)
 
          

轉換頭 益昌 100NT (後來娤上穴鑽, 開了第四個14mm厚35mm的杉木圓孔,
咬死鑽尾了. >< )


圓穴鋸 益昌300nt 可能買貴了但不同品牌 (這是鎢鋼鑽可鑽金屬, 買超規了, 只要木鑽就ok了)實際鑽14mm杉木 效率低還會冒煙(木削/穴 臭灰搭), 刀刃及鑽穴會卡木削



沒買


穴鑽套組, B&Q 210NT, 鑽14mm杉木ok

磨砂鑽頭 B&Q 95NT, 用來磨木材邊角及毛邊 ok

              銼刀
              鑿刀

鋸子 B&Q 150NT 便宜好用

           
wel-3000A 打孔圖  Note: 16mm/50mm 孔可縮小, 但要小心露孔, 及是否方便理線



WEL-3000A 安裝圖   Note: 1.鎖孔下方須加開一溝槽方便鎖腹走線放入
                                       2.鎖鎖仁時,可利用插入它門鑰匙,旋轉鎖仁入鎖
                                       3.安裝門鎖把時, 留意鎖腹走線是否干涉
                                       4.感應卡是與鎖腹內電路配對非與門把電路板配對
                                       5.沒有防塵盒及圓柱兩項材料

WEL-3000A user guide

完成圖: 杉木板厚14mm  完成品總厚 14mmx4= 56mm

參考
3. Waferlock 回覆: 用設定卡設子卡功能為單機版需更換鎖腹(3000nt), 台東有經銷商可辦理