Skip to main content

A Python3, async interface to the SimpliSafe API

Project description

🚨 simplisafe-python: A Python3, async interface to the SimpliSafeā„¢ API

Travis CI PyPi Version License Code Coverage Maintainability Say Thanks

simplisafe-python (hereafter referred to as simplipy) is a Python3, asyncio-driven interface to the unofficial SimpliSafeā„¢ API. With it, users can get data on their system (including available sensors), set the system state, and more.

NOTE: SimpliSafeā„¢ has no official API; therefore, this library may stop working at any time without warning.

SPECIAL THANKS: Original inspiration was obtained from https://github.com/greencoder/simplipy; thanks to Scott Newman for all the hard work!

PLEASE READ: Version 3.0.0 and Beyond

Version 3.0.0 of simplipy makes several breaking, but necessary changes:

  • Moves the underlying library from Requests to aiohttp
  • Changes the entire library to use asyncio
  • Makes 3.6 the minimum version of Python required

If you wish to continue using the previous, synchronous version of simplipy, make sure to pin version 2.0.2.

Installation

pip install simplisafe-python

Usage

Getting Systems Associated with an Account

simplipy starts within an aiohttp ClientSession:

import asyncio

from aiohttp import ClientSession


async def main() -> None:
    """Create the aiohttp session and run."""
    async with ClientSession() as websession:
      # YOUR CODE HERE


asyncio.get_event_loop().run_until_complete(main())

To get all SimpliSafeā„¢ systems associated with an account:

import asyncio

from aiohttp import ClientSession

from simplipy import get_systems


async def main() -> None:
    """Create the aiohttp session and run."""
    async with ClientSession() as websession:
      systems = await get_systems("<EMAIL>", "<PASSWORD>", websession)
      # >>> [<simplipy.system.SystemV2 object at 0x10661e3c8>, ...]


asyncio.get_event_loop().run_until_complete(main())

The System Object

System objects are used to retrieve data on and control the state of SimpliSafeā„¢ systems. Two types of objects can be returned:

  • SystemV2: an object to control V2 (classic) SimpliSafeā„¢ systems
  • SystemV3: an object to control V3 (new, released in 2018) SimpliSafeā„¢ systems

Despite the differences, simplipy provides a common interface to these objects, meaning the same properties and methods are available to both.

Properties and Methods

from simplipy import get_systems


async def main() -> None:
    """Create the aiohttp session and run."""
    async with ClientSession() as websession:
      systems = await get_systems("<EMAIL>", "<PASSWORD>", websession)
      # >>> [<simplipy.system.SystemV2 object at 0x10661e3c8>]

      for system in systems:
        # Return a reference to a SimpliSafeā„¢ account object (detailed later):
        system.account
        # >>> <simplipy.account.SimpliSafeā„¢ object at 0x12aba2321>

        # Return whether the alarm is currently going off:
        system.alarm_going_off
        # >>> False

        # Return a list of sensors attached to this sytem (detailed later):
        system.sensors
        # >>> [<simplipy.sensor.SensorV2 object at 0x10661e3c8>, ...]

        # Return the system's serial number:
        system.serial
        # >>> 1234ABCD

        # Return the current state of the system:
        system.state
        # >>> simplipy.system.SystemStates.away

        # Return the SimpliSafeā„¢ identifier for this system:
        system.system_id
        # >>> 1234ABCD

        # Return the SimpliSafeā„¢ version:
        system.version
        # >>> 2

        # Return a list of events for the system with an optional start timestamp and
        # number of events - omitting these parameters will return all events (max of
        # 50) stored in SimpliSafeā„¢'s cloud:
        await system.get_events(from_timestamp=1534035861, num_events=2)
        # >>> return {"numEvents": 2, "lastEventTimestamp": 1534035861, "events": [{...}]}

        # Set the state of the system:
        await system.set_away()
        await system.set_home()
        await system.set_off()

        # Get the latest values from the system; by default, include a refresh
        # of system info and use cached values (both can be overridden):
        await system.update(refresh_location=True, cached=True)


asyncio.get_event_loop().run_until_complete(main())

A Note on system.update()

There are two crucial differences between V2 and V3 systems when updating:

  • V2 systems, which use only 2G cell connectivity, will be slower to update than V3 systems when those V3 systems are connected to WiFi.
  • V2 systems will audibly announce, "Your settings have been synchronized." when the update completes; V3 systems will not. Unfortunately, this cannot currently be worked around.

The Sensor Object

Sensor objects provide information about the SimpliSafeā„¢ sensors to which they relate.

NOTE: Individual sensors cannot be updated directly; instead, the update() method on their parent System object should be used. It is crucial to remember that sensor values are only as current as the last time system.update() was called.

Like their System cousins, two types of objects can be returned:

  • SensorV2: an object to view V2 (classic) SimpliSafeā„¢ sensors
  • SensorV3: an object to view V3 (new, released in 2018) SimpliSafeā„¢ sensors

Once again, simplipy provides a common interface to these objects; however, there are some properties that are either (a) specific to one version or (b) return a different meaning based on the version. These differences are outlined below.

Base Properties

from simplipy import get_systems


async def main() -> None:
    """Create the aiohttp session and run."""
    async with ClientSession() as websession:
      systems = await get_systems("<EMAIL>", "<PASSWORD>", websession)
      for system in systems:
        for serial, sensor_attrs in system.sensors.items():
          # Return the sensor's name:
          sensor.name
          # >>> Kitchen Window

          # Return the sensor's serial number (either through the index or
          # through the property):
          serial
          # >>> 1234ABCD
          sensor.serial
          # >>> 1234ABCD

          # Return the sensor's type:
          sensor.type
          # >>> simplipy.sensor.SensorTypes.glass_break

          # Return whether the sensor is in an error state:
          sensor.error
          # >>> False

          # Return whether the sensor has a low battery:
          sensor.low_battery
          # >>> False

          # Return whether the sensor has been triggered:
          sensor.triggered
          # >>> False


asyncio.get_event_loop().run_until_complete(main())

V2 Properties

from simplipy import get_systems


async def main() -> None:
    """Create the aiohttp session and run."""
    async with ClientSession() as websession:
      systems = await get_systems("<EMAIL>", "<PASSWORD>", websession)
      for system in systems:
        for serial, sensor_attrs in system.sensors.items():
          # Return the sensor's data as a currently non-understood integer:
          sensor.data
          # >>> 0

          # Return the sensor's settings as a currently non-understood integer:
          sensor.settings
          # >>> 1


asyncio.get_event_loop().run_until_complete(main())

V3 Properties

from simplipy import get_systems


async def main() -> None:
    """Create the aiohttp session and run."""
    async with ClientSession() as websession:
      systems = await get_systems("<EMAIL>", "<PASSWORD>", websession)
      for system in systems:
        for sensor in system.sensors:
          # Return whether the sensor is offline:
          sensor.offline
          # >>> False

          # Return a settings dictionary for the sensor:
          sensor.settings
          # >>> {"instantTrigger": False, "away2": 1, "away": 1, ...}

          # For temperature sensors, return the current temperature:
          sensor.temperature
          # >>> 67


asyncio.get_event_loop().run_until_complete(main())

The Account Object

Each System object has a reference to an Account object. This object contains properties and a method useful for authentication and ongoing access.

VERY IMPORTANT NOTE: the Account object contains references to SimpliSafeā„¢ access and refresh tokens. It is vitally important that you do not let these tokens leave your control. If exposed, savvy attackers could use them to view and alter your system's state. You have been warned; proper usage of these properties is solely your responsibility.

from simplipy import get_systems


async def main() -> None:
    """Create the aiohttp session and run."""
    async with ClientSession() as websession:
      systems = await get_systems("<EMAIL>", "<PASSWORD>", websession)
      for system in systems:
        # Return the current access token:
        system.account.access_token
        # >>> 7s9yasdh9aeu21211add

        # Return the current refresh token:
        system.account.refresh_token
        # >>> 896sad86gudas87d6asd

        # Return the SimpliSafeā„¢ user ID associated with this account:
        system.account.user_id
        # >>> 1234567


asyncio.get_event_loop().run_until_complete(main())

Errors/Exceptions

simplipy exposes three useful error types:

  • simplipy.errors.SimplipyError: a base error that all other simplipy errors inherit from
  • simplipy.errors.RequestError: an error related to HTTP requests that return something other than a 200 response code
  • simplipy.errors.TokenExpiredError: an error related to an expired access token

Refreshing the Access Token

When simplipy.get_systems() is run, everything is set to make repeated authorized requests against the SimpliSafeā„¢ cloud. At some point, however, the access token will expire and any future requests will raise simplipy.errors.TokenExpiredError.

When this occurs, a new access token can easily be generated:

await system.account.refresh_access_token()

This will use the "on-file" refresh token to request a new access token; once the call is complete, you're good to go.

In some instances, it may be desirable to store the "on-file" refresh token for later use (for example, if your app/script/etc. stops and needs to restart at some indeterminate point in the future). In that case, the refresh_access_token() method can take an optional refresh_token parameter:

await system.account.refresh_access_token(refresh_token='abcdefg987665')

Although no official documentation exists, basic testing appears to confirm the hypothesis that the refresh token is both long-lived and single-use. This means that theoretically, it should be possible to use it to create an access token long into the future. If refresh_access_token() should throw an error, however, the system object(s) will need to be recreated via simplipy.get_systems.

Contributing

  1. Check for open features/bugs or initiate a discussion on one.
  2. Fork the repository.
  3. Install the dev environment: make init.
  4. Enter the virtual environment: pipenv shell
  5. Code your new feature or bug fix.
  6. Write a test that covers your new functionality.
  7. Run tests and ensure 100% code coverage: make coverage
  8. Add yourself to AUTHORS.md.
  9. Submit a pull request!

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

simplisafe-python-3.0.4.tar.gz (18.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

simplisafe_python-3.0.4-py3-none-any.whl (16.6 kB view details)

Uploaded Python 3

File details

Details for the file simplisafe-python-3.0.4.tar.gz.

File metadata

  • Download URL: simplisafe-python-3.0.4.tar.gz
  • Upload date:
  • Size: 18.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.0 pkginfo/1.4.2 requests/2.19.1 setuptools/40.4.3 requests-toolbelt/0.8.0 tqdm/4.26.0 CPython/3.7.0

File hashes

Hashes for simplisafe-python-3.0.4.tar.gz
Algorithm Hash digest
SHA256 88f0cf8928f8546844907885195e62545cd95bb2c89696c9a6bc39cd202a2d3e
MD5 8a4552dfad74bf219307755c801deb5a
BLAKE2b-256 03c4c7fdf793f8b1be8544410a16020a4c2c7bfe12199626706ebbfd61d0ed2c

See more details on using hashes here.

File details

Details for the file simplisafe_python-3.0.4-py3-none-any.whl.

File metadata

  • Download URL: simplisafe_python-3.0.4-py3-none-any.whl
  • Upload date:
  • Size: 16.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.0 pkginfo/1.4.2 requests/2.19.1 setuptools/40.4.3 requests-toolbelt/0.8.0 tqdm/4.26.0 CPython/3.7.0

File hashes

Hashes for simplisafe_python-3.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 ec751ba5758ac81fb1478465ed0d1acc10061bd31827d8e7758c044c37d00b76
MD5 4069af06b6cb0b03b7ad60b9ee39c4e6
BLAKE2b-256 98805952446a8f1c35319fa76e30cc778313c07e1b263170d8ddcddcdd3b2877

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page