""" Sensor for SigFox devices. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.sigfox/ """ import logging import datetime import json from urllib.parse import urljoin import requests import voluptuous as vol import homeassistant.helpers.config_validation as cv from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import CONF_NAME from homeassistant.helpers.entity import Entity _LOGGER = logging.getLogger(__name__) SCAN_INTERVAL = datetime.timedelta(seconds=30) API_URL = 'https://backend.sigfox.com/api/' CONF_API_LOGIN = 'api_login' CONF_API_PASSWORD = 'api_password' DEFAULT_NAME = 'sigfox' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_API_LOGIN): cv.string, vol.Required(CONF_API_PASSWORD): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, }) def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the sigfox sensor.""" api_login = config[CONF_API_LOGIN] api_password = config[CONF_API_PASSWORD] name = config[CONF_NAME] try: sigfox = SigfoxAPI(api_login, api_password) except ValueError: return False auth = sigfox.auth devices = sigfox.devices sensors = [] for device in devices: sensors.append(SigfoxDevice(device, auth, name)) add_devices(sensors, True) def epoch_to_datetime(epoch_time): """Take an ms since epoch and return datetime string.""" return datetime.datetime.fromtimestamp(epoch_time).isoformat() class SigfoxAPI(object): """Class for interacting with the SigFox API.""" def __init__(self, api_login, api_password): """Initialise the API object.""" self._auth = requests.auth.HTTPBasicAuth(api_login, api_password) if self.check_credentials(): device_types = self.get_device_types() self._devices = self.get_devices(device_types) def check_credentials(self): """"Check API credentials are valid.""" url = urljoin(API_URL, 'devicetypes') response = requests.get(url, auth=self._auth, timeout=10) if response.status_code != 200: if response.status_code == 401: _LOGGER.error( "Invalid credentials for Sigfox API") else: _LOGGER.error( "Unable to login to Sigfox API, error code %s", str( response.status_code)) raise ValueError('Sigfox component not setup') return True def get_device_types(self): """Get a list of device types.""" url = urljoin(API_URL, 'devicetypes') response = requests.get(url, auth=self._auth, timeout=10) device_types = [] for device in json.loads(response.text)['data']: device_types.append(device['id']) return device_types def get_devices(self, device_types): """Get the device_id of each device registered.""" devices = [] for unique_type in device_types: location_url = 'devicetypes/{}/devices'.format(unique_type) url = urljoin(API_URL, location_url) response = requests.get(url, auth=self._auth, timeout=10) devices_data = json.loads(response.text)['data'] for device in devices_data: devices.append(device['id']) return devices @property def auth(self): """Return the API authentification.""" return self._auth @property def devices(self): """Return the list of device_id.""" return self._devices class SigfoxDevice(Entity): """Class for single sigfox device.""" def __init__(self, device_id, auth, name): """Initialise the device object.""" self._device_id = device_id self._auth = auth self._message_data = {} self._name = '{}_{}'.format(name, device_id) self._state = None def get_last_message(self): """Return the last message from a device.""" device_url = 'devices/{}/messages?limit=1'.format(self._device_id) url = urljoin(API_URL, device_url) response = requests.get(url, auth=self._auth, timeout=10) data = json.loads(response.text)['data'][0] payload = data['data'] lat = data['rinfos'][0]['lat'] lng = data['rinfos'][0]['lng'] snr = data['snr'] epoch_time = data['time'] return {'lat': lat, 'lng': lng, 'payload': payload, 'snr': snr, 'time': epoch_to_datetime(epoch_time)} def update(self): """Fetch the latest device message.""" self._message_data = self.get_last_message() self._state = self._message_data['payload'] @property def name(self): """Return the HA name of the sensor.""" return self._name @property def state(self): """Return the payload of the last message.""" return self._state @property def device_state_attributes(self): """Return other details about the last message.""" return self._message_data