diff --git a/homeassistant/components/sms/gateway.py b/homeassistant/components/sms/gateway.py index 7a2c86d8ba95ba9595cb403cb9711a1a37b80858..000434561bc4b5f9fb3dedcbf43c3cb5eea069fe 100644 --- a/homeassistant/components/sms/gateway.py +++ b/homeassistant/components/sms/gateway.py @@ -6,6 +6,10 @@ from gammu.asyncworker import ( # pylint: disable=import-error, no-member GammuAsyncWorker, ) +from homeassistant.core import callback + +from .const import DOMAIN + _LOGGER = logging.getLogger(__name__) @@ -15,6 +19,120 @@ class Gateway: def __init__(self, worker, hass): """Initialize the sms gateway.""" self._worker = worker + self._hass = hass + + async def init_async(self): + """Initialize the sms gateway asynchronously.""" + try: + await self._worker.set_incoming_sms_async() + except gammu.ERR_NOTSUPPORTED: + _LOGGER.warning("Your phone does not support incoming SMS notifications!") + else: + await self._worker.set_incoming_callback_async(self.sms_callback) + + def sms_callback(self, state_machine, callback_type, callback_data): + """Receive notification about incoming event. + + @param state_machine: state machine which invoked action + @type state_machine: gammu.StateMachine + @param callback_type: type of action, one of Call, SMS, CB, USSD + @type callback_type: string + @param data: event data + @type data: hash + """ + _LOGGER.debug( + "Received incoming event type:%s,data:%s", callback_type, callback_data + ) + entries = self.get_and_delete_all_sms(state_machine) + _LOGGER.debug("SMS entries:%s", entries) + data = list() + + for entry in entries: + decoded_entry = gammu.DecodeSMS(entry) + message = entry[0] + _LOGGER.debug("Processing sms:%s,decoded:%s", message, decoded_entry) + if decoded_entry is None: + text = message["Text"] + else: + text = "" + for inner_entry in decoded_entry["Entries"]: + if inner_entry["Buffer"] is not None: + text = text + inner_entry["Buffer"] + + event_data = dict( + phone=message["Number"], date=str(message["DateTime"]), message=text + ) + + _LOGGER.debug("Append event data:%s", event_data) + data.append(event_data) + + self._hass.add_job(self._notify_incoming_sms, data) + + # pylint: disable=no-self-use + def get_and_delete_all_sms(self, state_machine, force=False): + """Read and delete all SMS in the modem.""" + # Read SMS memory status ... + memory = state_machine.GetSMSStatus() + # ... and calculate number of messages + remaining = memory["SIMUsed"] + memory["PhoneUsed"] + start_remaining = remaining + # Get all sms + start = True + entries = list() + all_parts = -1 + all_parts_arrived = False + _LOGGER.debug("Start remaining:%i", start_remaining) + + try: + while remaining > 0: + if start: + entry = state_machine.GetNextSMS(Folder=0, Start=True) + all_parts = entry[0]["UDH"]["AllParts"] + part_number = entry[0]["UDH"]["PartNumber"] + is_single_part = all_parts == 0 + is_multi_part = 0 <= all_parts < start_remaining + _LOGGER.debug("All parts:%i", all_parts) + _LOGGER.debug("Part Number:%i", part_number) + _LOGGER.debug("Remaining:%i", remaining) + all_parts_arrived = is_multi_part or is_single_part + _LOGGER.debug("Start all_parts_arrived:%s", all_parts_arrived) + start = False + else: + entry = state_machine.GetNextSMS( + Folder=0, Location=entry[0]["Location"] + ) + + if all_parts_arrived or force: + remaining = remaining - 1 + entries.append(entry) + + # delete retrieved sms + _LOGGER.debug("Deleting message") + state_machine.DeleteSMS(Folder=0, Location=entry[0]["Location"]) + else: + _LOGGER.debug("Not all parts have arrived") + break + + except gammu.ERR_EMPTY: + # error is raised if memory is empty (this induces wrong reported + # memory status) + _LOGGER.info("Failed to read messages!") + + # Link all SMS when there are concatenated messages + entries = gammu.LinkSMS(entries) + + return entries + + @callback + def _notify_incoming_sms(self, messages): + """Notify hass when an incoming SMS message is received.""" + for message in messages: + event_data = { + "phone": message["phone"], + "date": message["date"], + "text": message["message"], + } + self._hass.bus.async_fire(f"{DOMAIN}.incoming_sms", event_data) async def send_sms_async(self, message): """Send sms message via the worker.""" @@ -40,6 +158,7 @@ async def create_sms_gateway(config, hass): worker.configure(config) await worker.init_async() gateway = Gateway(worker, hass) + await gateway.init_async() return gateway except gammu.GSMError as exc: # pylint: disable=no-member _LOGGER.error("Failed to initialize, error %s", exc)