Category: 隨筆

  • 例行的清明掃墓,度了一個劫

    倒不是因為觸了霉頭,我這個人不信邪,我說過,就算是超自然的力量奪走我的生命,我到死也不會承認他的存在。

    上山的前一天開始發燒,不知道是吃錯了什麼東西,還是收拾家裡陳舊的物品時,帶起來陳舊的細菌或是病毒,特別是我媽留下放了十年的那一堆酵素,每次回家我都要把他們倒掉然後扔掉瓶子,因為實在是太多了。也許是傾倒的時候吸入了某些細菌或病毒。


    發燒的第一天我自己並沒有任何感覺,但是Apple Watch開始告警,夜間的心率升高,靜息呼吸頻率也變高,我沒注意。

    看來這個東西是很準確的,Apple Watch 是個好夥伴。

    清明上山的時候除了肚子有點痛,頭有點痛,腳發軟,覺得睏,打電話的時候居然跟Miley 一樣不自覺的蹲在了地上,沒有其他的感覺,其實應該已經是燒到38度以上了。

    燒到第三天,叫小姑送我去醫院,然後到了社區診所,醫生摸了下脈搏,開了退燒藥,然後用生薑蓋在肚皮上,艾灸了10分鐘。

    當天晚上開始心率狂跳,一直出汗,折騰整夜。(應該是身體的白血球在劇烈的進攻細菌)

    總之呢,燒了四天之後,去了醫院急診,開始轉入感染科住院,血液檢查提示有感染,開始IV 廣譜抗生素,

    血檢報告提示:

    尿檢未見異常

    糞便檢測未見異常

    PCR 病毒檢測未見異常

    武漢肺炎 病毒檢測未見異常

    沒有嘔吐,沒有腹瀉,沒有咳嗽,除了肚臍左側有一點點痛和發燒,沒有其他任何症狀。

    IV 兩天針對好氧菌和厭氧菌的不同抗生素,體溫不但沒有下降,反而上升了一點點,從37.6 到了38,護士已經拿來了退燒藥,說告訴我讓我吃就吃。

    CT 做完,沒有看出什麼問題,醫生試圖以不明原因發燒忽悠過去,於是我要求做增強CT,然後增強CT 做出來,還是沒看出來什麼問題。

    我想,看得出來問題的話,我大概就出不了院了。

    要求強制出院,準備趕回台灣,最多是在機場入境被攔下來然後救護車送去醫院,總比在這小山村的醫院什麼都查不出來好。

    小姑和小姑丈一直在照料我,因為大概率可能是吃了他們家的東西出的問題,但根本原因還是我脆弱的腸胃。

    計劃是,妹妹幫我叫了一台車直接送我去重慶的酒店。

    Miley 當晚飛到重慶等我,第二天和我一起飛回去。

    雖然精神還是沒有很好,畢竟燒了六天,但是感覺問題不大,小又一定要來找我,於是在酒店見面。

    小又終於是又離婚了,怎麼說呢,小又一直沒什麼自我獨立意識,從小家裡富裕,不缺錢,但是因為媽媽的原因,又對婚姻和男人有著傳統意義上的要求,大概是因為她爸爸在男女關係上為所欲為,所以她反而沒有為所欲為,而是走了一條非常傳統的道路,真是讓人意外,本來以為她可以像她爸爸一樣,成為交際花的,在她開那間酒吧的時候,這是我對她的期待,算是落空了。

    小王也離婚了,葳君和我都不意外,畢竟她幾乎每次見面都會說她要離婚,但付諸實施後,她似乎並不希望更多人知道這個消息,看起來她只告訴了少數幾個人,好在兩個兒子應該有很多事情需要她忙,並不會讓她過於空閒而至於最後要去看身心科。

    勇君現在是ByteDance 重慶的市場總監,每天有很多巴結他的人,以前的同學也有很多巴結他,畢竟隨便放點流量過去,就可以把那隻豬吹上風口。

    至於從軍隊退役的野人,聽說每次都騙勇君說兩個人吃飯,然後等勇君去了就會發現一群地方領導等著給他敬酒……

    抵達桃園機場的時候,並沒有被溫度探測儀檢測到體溫過高,因為已經沒有發燒了,只好回家,第二天去了臺大醫院,剛好還能掛上一個號。

    開了抽血單,第三天從健保app 上看了結果,所有的指標都已經恢復正常。

    台灣的血檢數值和中國還不一樣,但對於不是醫生的我來說,不過也是看個熱鬧罷了。

  • 如何使用中央氣象署的api 獲取當前天氣和預報

    我之前一直通過n8n 使用OpenWeatherMap 的api 來獲取天氣和預報並通過pushover 發送到手機,但是,由於台灣是海島型氣候,天氣多變,預報往往不太準確,而比較權威的the weather channel 又不提供api,中央氣象署的api 其實我很早就註冊了,之前沒有AI 幫忙,他們的文檔又非常讓人困擾,所以沒有使用。

    今天讓AI 看了一下他們的文檔,終於替換掉了OpenWeatherMap 的api 。

    中央氣象署開放資料平臺之資料擷取API – https://opendata.cwa.gov.tw/dist/opendata-swagger.html#/

    除了實時天氣之外,還有預報信息,由於台灣很小,所以可以具體到某一個區域,例如板橋區。

    但問題在於,板橋區的實時天氣和預報是兩個不同的api,而UV index 則不是在所有的氣象監測站都有,比如板橋區就沒有,新北只有一個。

    所以,為了生成這麼一條信息,需要調取三個api。

    當然,首先您需要去註冊一個中央氣象署會員 – https://pweb.cwa.gov.tw/emember/register/authorization

    註冊成功後,在會員資料部分,從“API授權碼” 中申請一個授權碼,您將會在後面的每一個api call 中使用他。

    我記得我註冊的時候他還是中央氣象局,升級署了。

    天氣預報api,板橋區監測站

    https://opendata.cwa.gov.tw/api/v1/rest/datastore/F-D0047-069?Authorization=YOUR-CWB-API-KEY&LocationName=板橋區

    實時天氣api,距離我最近的監測站

    https://opendata.cwa.gov.tw/api/v1/rest/datastore/O-A0001-001?Authorization=YOUR-CWB-API-KEY&StationId=C0AJ80

    UV index api, 只有比較大的站有。

    https://opendata.cwa.gov.tw/api/v1/rest/datastore/O-A0003-001?Authorization=YOUR-CWB-API-KEY&StationId=466881

    之所以使用三個api 是因為實時天氣我選了距離我最近的一個,如果您不需要那麼準確,可以只用新北區的監測站就可以。

    n8n 的 workflow 中,因為merge block 只支持兩個input,所以只能使用串行執行,如果只用兩個api 的話就可以merge ,稍快,但對於每個小時發送一次的天氣來說,這點時間差異其實沒有必要,串行就可以了。

    workflow 大概這個樣子。

    其中呢,code block 是把這三個api 的結果梳理一下,送給pushover。

    const obs = $('CWB Realtime Banqiao District').first().json.records.Station[0];
    const we = obs.WeatherElement;
    const fc = $('CWB Forecast Banqiao District').first().json.records.Locations[0].Location[0];
    const uvStation = $('CWA UV Index').first().json.records.Station[0].WeatherElement;
    
    const el = {};
    for (const e of fc.WeatherElement) el[e.ElementName] = e.Time;
    
    const at = el['體感溫度'];
    const pop = el['3小時降雨機率'];
    const wx = el['天氣現象'];
    const rh = el['相對濕度'];
    const ci = el['舒適度指數'];
    
    const days = {};
    for (const t of at) {
      const d = t.DataTime.slice(0, 10);
      if (!days[d]) days[d] = {};
      const h = t.DataTime.slice(11, 13);
      if (h === '06') days[d].morn = t.ElementValue[0].ApparentTemperature;
      if (h === '12') days[d].day = t.ElementValue[0].ApparentTemperature;
      if (h === '21') days[d].night = t.ElementValue[0].ApparentTemperature;
    }
    for (const t of wx) {
      const d = t.StartTime.slice(0, 10);
      if (!days[d]) days[d] = {};
      if (t.StartTime.slice(11, 13) === '12') days[d].wx = t.ElementValue[0].Weather;
    }
    for (const t of pop) {
      const d = t.StartTime.slice(0, 10);
      if (!days[d]) days[d] = {};
      if (t.StartTime.slice(11, 13) === '12') days[d].pop = t.ElementValue[0].ProbabilityOfPrecipitation;
    }
    for (const t of rh) {
      const d = t.DataTime.slice(0, 10);
      if (!days[d]) days[d] = {};
      if (t.DataTime.slice(11, 13) === '12') days[d].rh = t.ElementValue[0].RelativeHumidity;
    }
    for (const t of ci) {
      const d = t.DataTime.slice(0, 10);
      if (!days[d]) days[d] = {};
      if (t.DataTime.slice(11, 13) === '12') days[d].comfort = t.ElementValue[0].ComfortIndexDescription;
    }
    
    const dayTemp = {};
    for (const t of at) {
      const d = t.DataTime.slice(0, 10);
      const v = parseFloat(t.ElementValue[0].ApparentTemperature);
      if (!dayTemp[d]) dayTemp[d] = { min: v, max: v };
      if (v < dayTemp[d].min) dayTemp[d].min = v;
      if (v > dayTemp[d].max) dayTemp[d].max = v;
    }
    
    const sorted = Object.keys(days).sort();
    
    return [{json: {
      station: obs.StationName,
      weather: we.Weather !== '-99' ? we.Weather : uvStation.Weather,
      temp: we.AirTemperature,
      humidity: we.RelativeHumidity,
      wind: we.WindSpeed,
      windDir: el['風向'][0].ElementValue[0].WindDirection,
      rain: we.Now.Precipitation,
      pressure: we.AirPressure,
      high: we.DailyExtreme.DailyHigh.TemperatureInfo.AirTemperature,
      low: we.DailyExtreme.DailyLow.TemperatureInfo.AirTemperature,
      pop: pop[0].ElementValue[0].ProbabilityOfPrecipitation,
      comfort: ci[0].ElementValue[0].ComfortIndexDescription,
      feelsLike: at[0].ElementValue[0].ApparentTemperature,
      uv: uvStation.UVIndex,
      d0_morn: days[sorted[0]]?.morn || '-',
      d0_night: days[sorted[0]]?.night || '-',
      d1_wx: days[sorted[1]]?.wx || '-',
      d1_comfort: days[sorted[1]]?.comfort || '-',
      d1_min: dayTemp[sorted[1]]?.min || '-',
      d1_max: dayTemp[sorted[1]]?.max || '-',
      d1_rh: days[sorted[1]]?.rh || '-',
      d1_pop: days[sorted[1]]?.pop || '-',
      d2_wx: days[sorted[2]]?.wx || '-',
      d2_comfort: days[sorted[2]]?.comfort || '-',
      d2_min: dayTemp[sorted[2]]?.min || '-',
      d2_max: dayTemp[sorted[2]]?.max || '-',
      d2_rh: days[sorted[2]]?.rh || '-',
      d2_pop: days[sorted[2]]?.pop || '-',
    }}];

    在pushover 的block 中將這些output 整理成人類看得懂的樣子。

  • 作為一個身處自由世界的人,我這種講法是過於自私了

    我原本以為昨天晚上半夜被搖醒準備爬起來是幻覺,早上忽然看到,哎,有地震過。

    那看來不是幻覺。

    被社會主義奴役二十五年的委內瑞拉人民有沒有獲得自由還有待觀察,但以馬杜羅為首的軍方使用裝甲車衝撞輾壓抗議人群,早已失去其執政的合法性。

    至於他往美國境內走私毒品和軍火,並不是像墨西哥一樣從中國進口 fentanyl 原材料二次加工,而是哥倫比亞邊境過來的 cocaine ,日益先進的電動潛水艇技術讓美國廣袤的海岸線確實無法防範,就算是Coast Guard也會疲於奔命,關於這一點,Youtube 上有很多影片說明。

    中國支持馬杜羅的大量先進武器當然是為了石油而不是為了要和美國對抗,中共從來沒有什麼路線,底線,原則之類的,只有利益之爭。

    我至今還記得查韋斯女兒被一堆美鈔包圍的照片,這些所謂的社會主義革命者,其虛偽無恥無法用適當的文字來形容,查韋斯得癌症死去,算是為他留下一點顏面,如果長命百歲,他的名聲恐怕會更加惡臭。

    馬杜羅被美軍抓走後,網路上很多中國民族主義者想要共軍抓捕賴清德。

    對於民主國家而言,總統被抓了副總統可以上,副總統被抓了行政院長可以上,行政院長被抓了還有立法院院長,立法院副院長。

    而獨裁國家,總統被抓了,大家都不會往前衝的,你看委內瑞拉就知道。

    2026年觀看的第一部電影是關於二二八出品于1989年的《悲情城市》,這部電影對於二二八的描述並不直接。

    但是,想想今天的《大濛》都還遮遮掩掩,當年的《悲情城市》已經非常了不起,如果要放在一起比較,《大濛》就是0分。

    這部電影以1945至1949年為背景,透過基隆九份林家四兄弟的命運,呈現二二八事件與白色恐怖下臺灣社會的動盪與壓抑。四兄弟在政權更替、黑幫勾結與政治迫害中相繼遭遇不幸,象徵臺灣人民在歷史洪流中的掙扎,沉默與創傷。電影使用多種語言(台語、日語、粵語、國語、英語)呈現,以日常生活折射政治壓迫,並沒有什麼驚心動魄的場景。

    2023年出了4K 修復版,但是沒有上streaming ,大概是知道一上就會被盜版。

    今日的台灣社會對於二二八的反思是嚴重不足的,因為當時的那些上海黑幫和抓捕學生的兵,官,將並沒有得到任何清算。

    畢竟蔣公的日記已經把責任推給陳誠了。

    新的一年,願所有的獨裁者都長命百歲,這樣我們才能看到和民主國家不一樣的千百萬種可能性。