core/homeassistant/components/sigfox/sensor.py

161 lines
5.0 KiB
Python
Raw Normal View History

"""Sensor for SigFox devices."""
import datetime
import json
import logging
from urllib.parse import urljoin
import requests
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import CONF_NAME
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = datetime.timedelta(seconds=30)
2019-07-31 19:25:30 +00:00
API_URL = "https://backend.sigfox.com/api/"
CONF_API_LOGIN = "api_login"
CONF_API_PASSWORD = "api_password"
DEFAULT_NAME = "sigfox"
2019-07-31 19:25:30 +00:00
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_entities, 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_entities(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:
"""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."""
2019-07-31 19:25:30 +00:00
url = urljoin(API_URL, "devicetypes")
response = requests.get(url, auth=self._auth, timeout=10)
if response.status_code != 200:
if response.status_code == 401:
2019-07-31 19:25:30 +00:00
_LOGGER.error("Invalid credentials for Sigfox API")
else:
_LOGGER.error(
2019-07-31 19:25:30 +00:00
"Unable to login to Sigfox API, error code %s",
str(response.status_code),
)
raise ValueError("Sigfox integration not set up")
return True
def get_device_types(self):
"""Get a list of device types."""
2019-07-31 19:25:30 +00:00
url = urljoin(API_URL, "devicetypes")
response = requests.get(url, auth=self._auth, timeout=10)
device_types = []
2019-07-31 19:25:30 +00:00
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 = f"devicetypes/{unique_type}/devices"
url = urljoin(API_URL, location_url)
response = requests.get(url, auth=self._auth, timeout=10)
2019-07-31 19:25:30 +00:00
devices_data = json.loads(response.text)["data"]
for device in devices_data:
2019-07-31 19:25:30 +00:00
devices.append(device["id"])
return devices
@property
def auth(self):
"""Return the API authentication."""
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 = f"{name}_{device_id}"
self._state = None
def get_last_message(self):
"""Return the last message from a device."""
device_url = f"devices/{self._device_id}/messages?limit=1"
url = urljoin(API_URL, device_url)
response = requests.get(url, auth=self._auth, timeout=10)
2019-07-31 19:25:30 +00:00
data = json.loads(response.text)["data"][0]
payload = bytes.fromhex(data["data"]).decode("utf-8")
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()
2019-07-31 19:25:30 +00:00
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