對於安全性層級較高的裝置來說,通常配有警示通知系統,比如電池快耗盡、異常、或需使用者注意、介入時,裝置便會發出訊號來通知使用者,而 IDD Annunciation Status 就是負責通知 GATT Client 警示資訊,好讓 GATT Client 可以通報使用者。
IDD Annunciation Status 的資料欄位是根據警示類型而變動的,最短為 1 byte,表示沒有任何警示通知存在;最長可達 19 bytes。
欄位 | 說明 |
---|---|
Flags | 指示哪些欄位存在:Annunciation Present (對應 ID、Type、Status 欄位是否存在),AuxInfo1 Present~AuxInfo5 Present(對應 AuxInfo1~AuxInfo5 欄位是否存在) |
Annunciation Instance ID | 警示通知獨一無二的編號 |
Annunciation Type | 警示通知種類,如裝置須維修、藥劑耗盡等 |
Annunciation Status | 警示通知狀態,如待處理、睡眠中、已確認等 |
AuxInfo1~AuxInfo5 | 警示通知所需的輔助欄位 |
IDS 雖然定義了許多警示通知類型,但只有對 Temperature 通知給出了定義,也就是說,其它警示通知需要傳輸什麼資訊是由廠商自行定義,因此咱們只會設計基本的警示通知和 Temperature 通知。
首先來看看基本的警示通知需要哪些欄位。雖然一個最小的警示通知就是 Flags 為 0,表示沒有其他欄位存在,但這情況只出現在系統中沒有任何警示通知時,所以咱們會假設基礎警示通知中至少有 Annunciation 相關欄位,也就是 ID、Type 和 Status 這三個欄位都存在:
class BaseAnnunciation:
def __init__(
self,
type: int | None,
*,
flags: int | None = core.annunc.consts.ANNUNCIATION_PRESENT,
id: int | None = 0,
status: int | None = core.annunc.consts.STATUS_PENDING,
fired_timestamp: int = -1,
remaining_snoozed_time: int = 0,
):
self.flags = flags
self.id = id
self.type = type
self.status = status
# 被觸發時的系統計數器值(從系統啟動開始經過的秒數),
# 當系統計數器為此值時觸發,
# 小於 0 不觸發。
self.fired_timestamp = fired_timestamp
self.remaining_snoozed_time = remaining_snoozed_time
flags
、id
、type
和 status
都是 IDS 所明定,一般情況下,只須傳入 type
就好,因為 id
欄位會由系統給予,即使指定了 id
,也會被重設。fired_timestamp
表示當系統計數器為指定的數值時會觸發警示通知。這是為了測試而設,讓咱們可以藉由外部檔案來觸發警示通知。remaining_snoozed_time
表示警示通知還可以睡眠多少時間。雖然基礎警示通知不需要輔助欄位,但 Temperature 通知和廠商自訂的警示通知會需要,所以在 BaseAnnunciation
增加以下方法,然後需要輔助欄位的子類別可以覆寫這些方法:
class BaseAnnunciation:
def get_aux_info_1(self) -> int | None:
return None
def get_aux_info_2(self) -> int | None:
return None
def get_aux_info_3(self) -> int | None:
return None
def get_aux_info_4(self) -> int | None:
return None
def get_aux_info_5(self) -> int | None:
return None
而因為警示通知有已確認、待辦中和已睡眠等狀態,所以提供以下輔助方法:
class BaseAnnunciation:
def confirm(self):
self.status = core.annunc.consts.STATUS_CONFIRMED
def pend(self):
self.status = core.annunc.consts.STATUS_PENDING
def snooze(self, snoozing_time: int):
self.status = core.annunc.consts.STATUS_SNOOZED
self.remaining_snoozed_time = snoozing_time
然後為了能與 JSON 互相轉換,定義相關函數:
class BaseAnnunciation:
def to_dict(self):
return {
"flags": self.flags,
"id": self.id,
"type": self.type,
"status": self.status,
"fired_timestamp": self.fired_timestamp,
"remaining_snoozed_time": self.remaining_snoozed_time,
}
@classmethod
def from_dict(cls, d):
return cls(
d["type"],
flags=d["flags"],
id=d["id"],
status=d["status"],
fired_timestamp=d["fired_timestamp"],
remaining_snoozed_time=d["remaining_snoozed_time"],
)
要注意,這兩個方法 to_dict()
和 from_dict()
是屬於 BaseAnnunciation
類別,不像先前的 IDD Status 和 IDD Status Changed 等是寫在 Config
類別裡,所以咱們必須在 Config
類別增加相關成員變數:
class Config:
def __init__(self):
# Unit: seconds
self.annunc_snoozing_time = 180
self.annunc_ready: list[core.annunc.base.BaseAnnunciation] = []
self.annunc_fired: list[core.annunc.base.BaseAnnunciation] = []
annunc_snoozing_time
表示當使用者下達 Snooze 指令時,要讓警示通知睡眠多少秒。不過 IDS 在下達這指令時是以分計,會以秒來儲存只是為了方便程式計時。annunc_ready
表示還未觸發的新警示通知。annunc_fired
表示已觸發的警示通知。為了儲存成 JSON,咱們將資料轉成字典形式:
class Config:
def to_dict(self):
return {
"annunc_snoozing_time": self.annunc_snoozing_time,
"annunc_ready": [x.to_dict() for x in self.annunc_ready],
"annunc_fired": [x.to_dict() for x in self.annunc_fired],
}
接著提供方法將 JSON 還原為物件:
class Config:
@classmethod
def from_dict(cls, d: dict):
obj = cls()
obj.annunc_snoozing_time = d["annunc_snoozing_time"]
cls._restore_annunc(obj.annunc_ready.append, d["annunc_ready"])
obj.annunc_ready.sort(
key=lambda x: x.fired_timestamp if x.fired_timestamp is not None else -1
)
cls._restore_annunc(obj.annunc_fired.append, d["annunc_fired"])
return obj
@staticmethod
def _restore_annunc(fn, d):
for x in d:
if x["type"] == core.annunc.consts.TEMPERATURE:
annunc = core.annunc.temperature.TemperatureAnnunciation.from_dict(x)
else:
annunc = core.annunc.base.BaseAnnunciation.from_dict(x)
fn(annunc)
_restore_annunc()
_restore_annunc()
,會根據 type
來建置警示通知物件from_dict()
,annunc_ready
建置完後,必須依 fired_timestamp
來排序,以方便日後取用。需要自行執行排序是因為設定檔是由人所撰寫,難保不會出錯。基礎警示通知完成了,但咱們的任務還有 Temperature 通知和管理警示通知的設計,這些就待下回分解吧!