巴拿馬 波魁特 瑪瑪卡特莊園 水洗 古優提比卡老欉 奧莉亞卡納薩爾地塊

因緣際會,人生難以預料。本來只喝可樂的我,十年前因為工作的關係,開始喝咖啡,從一開始對咖啡毫無興趣,到現在每天要喝個三杯精品咖啡才剛好。這個真是難以預料也難以想像。

要喝精品咖啡,免不了需要找好豆子。我有個多年的老友,只愛烘豆跟寫程式,好東西就應該要跟大家分享,所以推廣這事就交給我來了~

在這邊介紹朋友自家烘焙的這款新鮮好豆子,很好喝喔~ 我老婆本來不愛精品咖啡豆的,她對精品咖啡老是覺得,太酸了,不喜歡!然而今天特地泡了這款豆子,她喝了眼睛瞪很大:「這個可以喔~趕快買」

呵呵,我的規劃成功了,因為據友人說,要不酸,就別找非洲豆,然後最好是水洗,還有剛烘好幾天內的,酸味也還沒出來。接著我打算沖濃一點,所以我再使用自家的 SAMANTHA 機器,尋找接近 1:12 的沖泡配方,最後找了蔦屋的配方來進行沖泡,設定好之後就讓機器完成接下來的工作,完全不用自己動手,輕鬆搞定 ^^

話說,喝到好喝,也來作點功課吧,到底是什麼樣的豆子呢?爬文了幾下,恩….

看來這個瑪瑪卡特莊園,貌似是個年輕的莊園,但是常出產 CP 值高的豆子的莊園。也常常得到最佳巴拿馬(Best of Panama)競賽。

同時,該莊園也有賣藝妓豆(Geisha)耶~ 下次來A我的友人買個幾斤烘來喝喝看。 ^___^

以下幾篇該莊園的介紹,供有興趣想更進一步了解的人參考:

用GPS種出的高品質咖啡豆! 你不能錯過的莊園-巴拿馬瑪瑪卡特莊園

巴拿馬 波魁特 瑪瑪卡特莊園 古優提比卡老欉 水洗 69BC號批次

巴拿馬 波魁特 瑪瑪卡特莊園 日曬(半磅)咖啡豆

最後,歡迎想一起當分母的朋友,請到我們的蝦皮下單,快點把這批喝完,就可以再來訂下一批豆子囉~

橘昇咖啡
蝦皮賣場

如果有想喝什麼豆子的,也歡迎與我們連絡,數量夠多的話就可以立刻成行喔~~

World of Coffee 2019 Berlin


很興奮的來到柏林參加歐洲2019咖啡展~ 第一次來到以精密重機械/高級汽車等聞名全球的德國,而且是其首都柏林參展。聽說德國人很嚴謹,人都很嚴肅,而且要求精準~

仔細一想,德國對大部份旳台灣人來說,應該是個很陌生的地方,除了賓士/BMW/雙人牌/梅克爾之外,還有什麼?上次看到德國的電影,聽到德國的音樂,是什麼時候?在戰後他們經歷了是什麼樣的生活?不像是鄰國日本韓國,新聞多少會播,德國?不知道… 不清楚…

不過,真的來到柏林之後的感覺,怎麼說…
意外的不熱鬧~ 新北市的房子都比這邊高大很多很多~
意外的德國生活還蠻悠閒的~ 大家怎麼好像都像在度假~ 慢慢來~
意外的大部份人都蠻友善的,我們迷路10鐘內就有3個人來指路,嚴肅的德國人在那裡?

而且,更意外的,大部份的計程車,都不會輸給 TAXI 電影裡的拼勁跟氣魄!車跟車間的距離可以少於5公分刷過去~然後搞到差點撞到再互比手勢,哇賽!短短不到20分鐘的車程,可以讓我二同事都暈車~ 我是覺得像坐雲霄飛車一樣刺激,不過我也不愛雲霄飛車… 我說, TAXI 的國別弄錯了吧!?應該是德國才對!

今年應意大利廠商邀約,我們來柏林展出我們所研發的 IoT 手沖咖啡機 :

HIROIA JIMMY
HIROIA SAMANTHA

對於以意式咖啡為主的歐洲,IoT手沖咖啡機相對是新鮮的新玩意,所以也來了不少人想要了解。而 JIMMY 的設計在以時尚為日常的歐洲來說,也絲毫不會遜色,還是要感謝設計師 Jimmy 的堅持,雖然也是帶給硬體 / 韌體 / APP 團隊不少的挑戰~ 嗯,大家都差點沒有翻臉,但是看到客戶的反應還是必需說是值得的。Jimmy~ You are a barstard~ but you are right.

第一次跟意大利的團隊合作到柏林參展,真的是有趣的體驗,90%他們說的都是義大利語跟德語?反正都聽不懂也分不清楚,不過有點自毫的是發現我的破英文在這裡也不會輸給許多在地德國人,因為他們講得更爛,真意外~ 巴士司機不太會講,計程車司機也不太會講,連餐廳女待也不是每個都會講~ 或者是他們不肯講? 這就不得而知了,文化跟歷史更深層的刻印,不是我們三五天的過客能輕易的了解的。總之,我發現我的英文比很多德國人好,開心~

HIROIA & Nouva Ricombi @ Berlin
不過,意大利人的熱情跟團結真是沒話說,參展還沒展完,晚上就開始開趴囉~~ 包下整個大樓最高二層,在微風與夕陽下,輕快的音樂,各式各樣的雞尾酒,隨著時間越晚,人越來越多,嘰哩呱啦、嘰嘰喳喳、吧啦吧啦,雖然都聽不懂(全是意大利文),但是十分歡樂的感覺不用聽懂也能體會~ 這樣作生意真好,為什麼那麼多台灣傳產要用痛苦的態度來工作呢?
對了,今年柏林世界咖啡賽台灣選手參賽的有拉花、杯測、跟咖啡調酒三項。很可惜前二項都在八強止步,但是現場好HIGH呀~希望台灣選手們再繼續努力為國爭光~ 加油加油。更新:因為計分錯誤,拉花後來台灣改列第二名,太棒了!
不能不提 La Marzocco 就是氣派,先在門口停了輛紅車然後刻上他們最火紅的 KB90 意式咖啡機,這台貴鬆鬆的機器可是大家搶著買,展場裡除了自己有作意式機的之外,我看其它的幾乎都用上這台機器了~ 結果他們還很誇張的,Booth 擺在入場前~
烘豆機名牌 PROBAT 這裡可是他的主場,少不了要出來秀一下
杯子也可以擺得很美,不愧是歐洲
TONE 也是作 IoT 咖啡題材的公司,T Shirt 跟 Logo 作得不錯,機器看起來很厲害,沒試喝不知道味道如何~
另外不得不提這家用水彩渲染的方式來表達每個產地咖啡的風味,很美,令人印象深刻!
烘豆機、烘豆機、更多的烘豆機
意式咖啡機、意式咖啡機、更多的意式咖啡機,這裡是意式咖啡機的大本營

2019歐洲咖啡展順利結束,能得到不少的民眾的欣賞由衷感謝~回家後再與團隊再加油把產品與服務作得更好~

展後隔天,週日的柏林,又是另一個意外~ 除了咖啡廳之外,就只有教堂的鐘聲跟觀光客沒有休息了… WHAT!? Let me in please! any shop will do.

食尚玩家裡有說過,「每段旅程總會有些意外,所以才會有趣」,所以才會有理由再來~

雖然該節目已經結束了,但是食尚玩家的精神還是會永遠留在粉絲的心中~ 「整個世界都是我的遊樂場」,是吧?咦,他們沒說過嗎?應該有吧?沒有嗎?

Orange Chang, June 9, 2019 柏林隨筆

DynamoDB PHP Sample Codes

AWS 工具組功能強大,而且功能還以火箭速度不斷開發,每年 AWS 的研發支出還力壓 Google。 不過要說 AWS 缺點的話也不是沒有…

一個是它是 Amazon WEB Service, 所以沒有網路的話就完全無用,這聽來是癈話,但是在 IoT 應用方面卻是一大問題,因為 IoT Device 的網路連線不可能是 100% 暢通的。所以 Greengrass 技術是十分重要的…. 我期待Greengrass 技術繼續發展下去。

另一個缺點是,因為功能出得太快太新了,線上文件也不是很好看,而且因為 AWS 同時支援數個語言,像是 DynamoDB 就有 Java 、JavaScript 、Node.js、.NET、PHP、Python、Ruby,還有iOS SDK、 Android SDK …等。
支援的程式語言很多是好事,但是也引發了一個問題:要嘛很難查到線上範例,要嘛勉強查到的範例卻不是自己使用的語言,雖然可以參考但是還是不是那麼方便。我自己就有無數次到 Stack Overflow 去查問題,結果要嘛只查到問題無人回答,要嘛竟然連問題都沒有的情況。

在這裡分享許多我用過的 PHP example codes,對於要使用 PHP 撰寫 DynamoDB 程式的開發者應該多少會有點幫助:

AWS helper function

function array_from_aws_result($awsArray)
{
	$data_table = array();
	foreach($awsArray as $k => $v){

		if(array_key_exists('S',$v)){
			$data_table[$k]=$v['S'];
		}
		if(array_key_exists('N',$v)){
			$data_table[$k]=floatval($v['N']);
		}
	}
	return $data_table;
}

Scan all records

do{
    $request = [
        'TableName' => 'IotecUser'
    ];

    if(isset($result) && isset($result['LastEvaluatedKey'])) {
        $request['ExclusiveStartKey'] = $result['LastEvaluatedKey'];
    }
    $result = $dynamoDbClient -> scan($request);

    foreach ($result['Items'] as $record) {
        $list_table = array_from_aws_result($record);
    }
}while(isset($result['LastEvaluatedKey']));
if(count($list_table) != 0){
	// process the list table
}else{
	// record not found
}

Scan table using query index. Single attribute index.

Partition Key: s_deviceId
Paramter: $deviceId

do{
    $request = [
        'TableName' => 'DeviceList',
        'IndexName' => 's_deviceId-index',                      
        'KeyConditionExpression' => 's_deviceId = :v_deviceId ',
        'ExpressionAttributeValues' =>
            [
                ':v_deviceId' => ['S' => $deviceId]
            ]
    ];

    if(isset($result) && isset($result['LastEvaluatedKey'])) {
        $request['ExclusiveStartKey'] = $result['LastEvaluatedKey'];
    }
    $result = $dynamoDbClient -> query($request);

    foreach ($result['Items'] as $record) {
        $list_table = array_from_aws_result($record);
    }
}while(isset($result['LastEvaluatedKey']));

if(count($list_table) != 0){
	// process the list table
}else{
	// record not found
}

Scan table using query index. Double attributes index.

Partition Key: n_certified

do{
    $request = [
        'TableName' => 'IotecPost',
        'IndexName' => 'n_certified-n_view_count-index',                      
        'KeyConditionExpression' => 'n_certified = :v_certified ',
        'ScanIndexForward' => false, // switch forward or backward
        'FilterExpression' => 's_country_code = :v_country_code',
        'ExpressionAttributeValues' =>
            [
                ':v_certified' => ['N' => '1'],
                ':v_country_code' => ['S' => 'TW']
            ]
    ];


    if(isset($result) && isset($result['LastEvaluatedKey'])) {
        $request['ExclusiveStartKey'] = $result['LastEvaluatedKey'];
    }
    $result = $dynamoDbClient -> query($request);

	$list_table[] = array();
    foreach ($result['Items'] as $record) {
        $item_table = array();
       
        foreach($record as $k=>$v){
            if(array_key_exists('S',$v)){
                $item_table[$k]=$v['S'];
            }
            if(array_key_exists('N',$v)){
                $item_table[$k]=floatval($v['N']);
            }
        }
        $list_table[] = $item_table;
    }
// if LastEvaluatedKey is set, means this is no the last record. Continue next query
}while(isset($result['LastEvaluatedKey'])); 

if(count($list_table) != 0){
	// process the list table
}else{
	// record not found
}

Get a dedicate record using getItem with Partition key.

Partition key: n_post_sn

$result = $dynamoDbClient->getItem(array(
    'ConsistentRead' => true,
    'TableName' => 'IotecPost',
    'Key'       => array(
        'n_post_sn'   => array('N' => '')
    )
));
$postTable = array_from_aws_result($result['Item']);
var_dump($postTable);

Get a dedicate record using getItem with Partition key and sort key.

Partition key: n_post_sn
Sort key: s_email

$result = $dynamoDbClient->getItem(array(
    'ConsistentRead' => true,
    'TableName' => 'IotecPost',
    'Key'       => array(
        's_email'   => array('S' => ''.$s_email),
        'n_post_sn' => array('N' => ''.$n_post_sn)
    )
));

Update record with updateItem

Partition key: n_post_sn
Sort key: s_email

Parameter: $userEmail,$postSn, $attributeName, $attributeType, $attributeValue

$dynamoDbClient->updateItem(array(
    'TableName' => 'IotecPost',
    'Key' => array(
       's_email'       => array('S' => ''.$userEmail),
       'n_post_sn'     => array('N' => ''.$postSn)
    ),
    "UpdateExpression" => "SET #key = :val",
    "ExpressionAttributeNames" => ['#key' => $attributeName],
    "ExpressionAttributeValues" => [':val' => [$attributeType => ''.$attributeValue]]
));

Delete record with deleteItem

Partition key: n_post_sn
Sort key: s_email

Parameter: $userEmail,$postSn

$dynamoDbClient->deleteItem(array(
    'TableName' => 'IotecPost',
    'Key' => array(
       's_email'       => array('S' => ''.$userEmail),
       'n_post_sn'     => array('N' => ''.$postSn)
    )
));

Remove attribute

Partition key: n_post_sn
Sort key: s_email

Parameter: $userEmail,$postSn

$result = $dynamoDbClient->updateItem(array(
    'TableName' => 'IotecPost',
    'Key' => array(
       's_email'       => array('S' => ''.$userEmail),
       'n_post_sn'     => array('N' => ''.$postSn)
    ),
    "UpdateExpression"    => "REMOVE s_address2"
));

DynamoDB PHP 最常見的坑

參數不接受空字串!參數不接受空字串!參數不接受空字串!

因為太重要了,要重覆三次

舉例如下:

Partition key: s_email
Parameter: $userName, $userEmail

$dynamoDbClient->updateItem(array(
    'TableName' => 'IotecUser',
    'Key' => array(
       's_email'       => array('S' => ''.$userEmail)
    ),
    "UpdateExpression" => "SET #key = :val",
    "ExpressionAttributeNames" => ['#key' => 's_name'],
    "ExpressionAttributeValues" => [':val' => ['S' => ''.$userName]]
));

上面的 code, 在 $userName = ” 的情況下,會發生錯誤

… One or more parameter values were invalid: An AttributeValue may not contain an empty string  …

只要看到上述這個錯誤訊息,八成又發生某個參數是空字串了