Skip to main content

Python SDK for controlling SIMO.io smart homes over REST+MQTT

Project description

simo-sdk

Quick start

from simo_sdk import SIMOClient

home = SIMOClient(
    url="https://hub.example.com",
    secret_key="YOUR_SIMO_IO_SECRET_KEY",
    instance="my-home",  # instance slug OR instance uid
)

light = home.components.filter(name="Main light", zone="Living Room")[0]
light.turn_on()
light.wait_for(fields=["value"], timeout=10)
print(light.value)

print(home.sun.is_night())

print(home.main_state.value)
print(home.weather.value)

Install

pip install -e packages/simo-sdk

SIMOClient

Type: simo_sdk.client.SIMOClient

home = SIMOClient(url="https://hub.example.com", secret_key="...", instance="my-home")

Parameters:

  • url: str – URL of your SIMO.io hub
  • secret_key: str – your SIMO.io user secret key
  • instance: str – instance slug or instance uid
  • verify_ssl: bool | str | None – optional (LAN hubs with self-signed certs are supported automatically)

If MQTT is not reachable, the client keeps retrying in the background.

Attributes:

  • home.zones: simo_sdk.collections.Zones
  • home.categories: simo_sdk.collections.Categories
  • home.components: simo_sdk.collections.Components
  • home.users: simo_sdk.collections.Users
  • home.sun: simo_sdk.sun.LocalSun
  • home.main_state: simo_sdk.models.Component | None
  • home.weather: simo_sdk.models.Component | None

home.sun

Type: simo_sdk.sun.LocalSun

Use this for time-of-day logic (sunrise/sunset/night checks) in automations.

Methods:

  • now() -> datetime
    • Current time in the instance timezone (if available).
  • is_night(localdatetime=None) -> bool
    • True when current time is outside sunrise–sunset.
  • get_sunrise_time(localdatetime=None) -> datetime
  • get_sunset_time(localdatetime=None) -> datetime
  • seconds_to_sunrise(localdatetime=None) -> float
  • seconds_to_sunset(localdatetime=None) -> float

Examples:

if home.sun.is_night():
    print("It's dark")

print("sunrise:", home.sun.get_sunrise_time(home.sun.now()))
print("sunset:", home.sun.get_sunset_time(home.sun.now()))

home.main_state

Type: simo_sdk.models.Component | None

This is the SIMO.io "Main State" component (default name: "Main State").

It represents a single global state for the whole smart home and is meant to be used by automations as a shared context.

Common states include (by default):

  • morning
  • day
  • evening
  • night
  • sleep
  • away
  • vacation

Usage:

state = home.main_state.value if home.main_state else None
if state in ("away", "vacation"):
    print("Skip some automations")

# change state (use configured state slugs)
if home.main_state:
    home.main_state.send("sleep")

home.weather

Type: simo_sdk.models.Component | None

This is the SIMO.io "Weather" component.

It provides current outdoor weather information for your instance location. In automations it is typically used to check:

  • temperature / feels_like
  • wind
  • rain/snow indicators
  • general condition

Usage:

if home.weather:
    w = home.weather.value or {}
    temp = (w.get("main") or {}).get("temp")
    feels = (w.get("main") or {}).get("feels_like")
    condition = ((w.get("weather") or [{}])[0] or {}).get("main")
    print(temp, feels, condition)

Typical home.weather.value shape (example):

{
  "dt": 1766408550,
  "id": 597881,
  "cod": 200,
  "sys": {
    "id": 1880,
    "type": 1,
    "sunset": 1766411955,
    "country": "LT",
    "sunrise": 1766386119
  },
  "base": "stations",
  "main": {
    "temp": 4.33,
    "humidity": 88,
    "pressure": 1023,
    "temp_max": 4.33,
    "temp_min": 4.33,
    "sea_level": 1023,
    "feels_like": 2.45,
    "grnd_level": 1014
  },
  "name": "Kulautuva",
  "wind": {
    "deg": 304,
    "gust": 3.26,
    "speed": 2.13
  },
  "coord": {
    "lat": 54.9383,
    "lon": 23.6476
  },
  "clouds": {
    "all": 36
  },
  "weather": [
    {
      "id": 802,
      "icon": "03d",
      "main": "Clouds",
      "description": "scattered clouds"
    }
  ],
  "timezone": 7200,
  "visibility": 10000
}

Zones

Zone type: simo_sdk.models.Zone

  • id: int
  • name: str

Lookup:

zone_by_id = home.zones[12]
zone_by_name = home.zones["Kitchen"]

Categories

Category type: simo_sdk.models.Category

  • id: int
  • name: str

Lookup:

cat_by_id = home.categories[5]
cat_by_name = home.categories["Lights"]

Components

Get by id

lamp = home.components[123]

Filter

Type returned by filter(...): simo_sdk.collections.ComponentQuery (iterable)

# by name (substring match, case-insensitive)
items = home.components.filter(name="light")

# by base_type
items = home.components.filter(base_type="switch")

# by zone/category using name
items = home.components.filter(zone="Kitchen", category="Lights")

# by zone/category using objects
z = home.zones["Kitchen"]
c = home.categories["Lights"]
items = home.components.filter(zone=z, category=c)

Filter parameters:

  • name: str | None
  • base_type: str | None
  • zone: int | str | simo_sdk.models.Zone | None
  • category: int | str | simo_sdk.models.Category | None

Order

items = home.components.filter(zone="Kitchen")

# default ordering is by id
items = items.order_by()          # same as order_by("id")

# order by any field
items = items.order_by("name")
items = items.order_by("-last_change")

# multi-field ordering
items = items.order_by("zone_id", "name")

Component

Component type: simo_sdk.models.Component

Actions

lamp.turn_on()
lamp.turn_off()
lamp.toggle()

blinds = home.components.filter(base_type="blinds")[0]
blinds.open()
blinds.close()

lamp.send(True)
lamp.call("set_volume", 30)

If the method exists on the component, you can call it directly:

lamp.set_volume(30)

Slave components

If your component has slave components (for example a multi-switch), you can control a slave like this:

multi = home.components.filter(name="Kitchen switch")[0]
slave = multi.slave(multi.slaves[0])
slave.turn_on()

Waiting for a state update

lamp.turn_on()
lamp.wait_for(fields=["value"], timeout=10)
print(lamp.value)

Reacting to changes

def changed(c):
    print(c.name, c.value)

lamp.on_change(changed, fields=["value"])

You can also receive the actor:

def changed(c, actor):
    if actor and actor.type == "user" and actor.user:
        print("changed by:", actor.user.name)
    else:
        print("changed by:", actor.type)

lamp.on_change(changed, fields=["value"])

Refresh

lamp.refresh()
print(lamp.value)

Fields

These are exposed as attributes:

  • id: int
  • name: str
  • icon: str | None
  • base_type: str | None
  • zone_id: int | None
  • category_id: int | None
  • gateway_id: int | None
  • show_in_app: bool | None
  • controller_uid: str | None
  • last_change: float | None
  • last_modified: float | None
  • read_only: bool | None
  • slaves: list[int]
  • info: Any
  • value: Any
  • value_units: str | None
  • meta: dict
  • config: dict
  • alive: bool | None
  • error_msg: str | None
  • alarm_category: str | None
  • arm_status: str | None
  • battery_level: int | None
  • controller_methods: list[str]

Additional fields (including future SIMO.io fields) are always available here:

  • data: dict

Users

User type: simo_sdk.models.User (this represents an InstanceUser in SIMO.io)

Current user

me = home.users.me
print(me.name, me.at_home)

Filter

home_users = home.users.filter(is_active=True)
people_at_home = home.users.filter(at_home=True)

Notify a user

u = home.users.filter(name="Simon")[0]
u.notify(severity="warning", title="Hello", body="Test")

Reacting to user changes

def presence_changed(u):
    print(u.name, "at_home:", u.at_home)

home.users.me.on_change(presence_changed, fields=["at_home"])

User fields

  • id: int (InstanceUser id)
  • user_id: int | None (global User id)
  • email: str | None
  • name: str | None
  • role_id: int | None
  • role_name: str | None
  • role_is_owner: bool | None
  • role_is_superuser: bool | None
  • role_can_manage_users: bool | None
  • role_is_person: bool | None
  • is_active: bool | None
  • at_home: bool | None
  • last_seen: float | None (timestamp)
  • last_seen_location: str | None ("lat,lon")
  • last_seen_speed_kmh: float | None
  • phone_on_charge: bool | None

Actor

Actor type: simo_sdk.models.Actor

  • type: str – one of: user, device, system, ai
  • user: simo_sdk.models.User | None

Note: actor.user is only set when actor.type == "user".

Project details


Download files

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

Source Distribution

simo_sdk-1.1.1.tar.gz (19.2 kB view details)

Uploaded Source

Built Distribution

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

simo_sdk-1.1.1-py3-none-any.whl (18.8 kB view details)

Uploaded Python 3

File details

Details for the file simo_sdk-1.1.1.tar.gz.

File metadata

  • Download URL: simo_sdk-1.1.1.tar.gz
  • Upload date:
  • Size: 19.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.0.1 CPython/3.12.3

File hashes

Hashes for simo_sdk-1.1.1.tar.gz
Algorithm Hash digest
SHA256 2f1678ef381c3f7668b89702c57a9a7af35a8e51a5b9ac82889ed9c1e6c32ed4
MD5 26ccddcb41c3c0f32e7b43f82a480793
BLAKE2b-256 bc2c57edbc7b518734887ddd633454100fdee16f358c48ea1364de9cdf1f2e2d

See more details on using hashes here.

File details

Details for the file simo_sdk-1.1.1-py3-none-any.whl.

File metadata

  • Download URL: simo_sdk-1.1.1-py3-none-any.whl
  • Upload date:
  • Size: 18.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.0.1 CPython/3.12.3

File hashes

Hashes for simo_sdk-1.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9a91620584c0b581af71e002e5d6d3dee129975fc4384210a91565ea99f824f8
MD5 caa38f98c54c4e69f510e7a5f6e634f5
BLAKE2b-256 d2b58750eca2c967a394c80a72143d88327d7ca22db9972ef871b6a4bd413207

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