Skip to content

Commit 34a4f25

Browse files
authored
Merge pull request #82 from sinricpro/5.0.0-dev
feat: Send a device setting event to SinricPro
2 parents 77b361f + 2bc96f9 commit 34a4f25

File tree

11 files changed

+708
-3
lines changed

11 files changed

+708
-3
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## [5.2.0]
2+
- feat: Send a device setting event to SinricPro
3+
4+
## [5.1.1]
5+
- feat: Module settings commands.
6+
- fix: Missing scope in response.
7+
- fix: Missing mac in websocket header.
8+
19
## [5.0.1]
210
- fix: [Periodic disconnects of WebSocketClient](https://github.com/sinricpro/python-sdk/issues/79)
311

examples/settings/device/README.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Device Settings Example
2+
3+
This example demonstrates how to handle **device-level settings** in SinricPro.
4+
5+
## Device Settings vs Module Settings
6+
7+
SinricPro supports two types of settings:
8+
9+
### Device Settings
10+
- Configuration for **this specific device**
11+
- Registered via: `device.on_setting(callback)`
12+
- Examples: Tilt angle, speed, direction, auto-close timeout
13+
- Callback signature: `async def callback(setting_id: str, value: Any) -> bool`
14+
- Settings are configured per-device in the SinricPro portal
15+
16+
### Module Settings
17+
- Configuration for the **module (dev board)** itself
18+
- Registered via: `sinric_pro.on_set_setting(callback)`
19+
- Examples: WiFi retry count, log level, heartbeat interval
20+
- Callback signature: `async def callback(setting_id: str, value: Any) -> bool`
21+
22+
## Usage
23+
24+
```python
25+
from sinricpro import SinricProBlinds
26+
27+
blinds = SinricProBlinds("your-device-id")
28+
29+
# Register device-level setting callback
30+
async def on_device_setting(setting_id: str, value: Any) -> bool:
31+
if setting_id == "tilt":
32+
set_blinds_tilt(value)
33+
return True
34+
elif setting_id == "speed":
35+
set_motor_speed(value)
36+
return True
37+
return False
38+
39+
blinds.on_setting(on_device_setting)
40+
```
41+
42+
## Setting Value Types
43+
44+
Device settings can have different value types:
45+
46+
| Setting | Type | Example Values |
47+
|---------|------|----------------|
48+
| `tilt` | Integer | 0-100 |
49+
| `direction` | String | "up", "down" |
50+
| `speed` | String | "slow", "normal", "fast" |
51+
| `auto_close` | Boolean | true, false |
52+
| `close_timeout` | Integer | 60-3600 (seconds) |
53+
54+
## Running the Example
55+
56+
1. Replace `YOUR_DEVICE_ID_HERE` with your actual device ID
57+
2. Set environment variables or replace credentials:
58+
```bash
59+
export SINRICPRO_APP_KEY="your-app-key"
60+
export SINRICPRO_APP_SECRET="your-app-secret"
61+
```
62+
3. Run the example:
63+
```bash
64+
python device_settings_example.py
65+
```
66+
67+
## Configuring Settings in SinricPro Portal
68+
69+
Device settings are configured in the SinricPro portal:
70+
71+
1. Go to [sinric.pro](https://sinric.pro)
72+
2. Navigate to your device
73+
3. Click on "Settings" tab
74+
4. Add or modify settings with their IDs and values
75+
5. Save changes - the SDK will receive the new values via the callback
76+
77+
## Best Practices
78+
79+
1. **Validate Values**: Always validate setting values before applying them
80+
2. **Type Checking**: Check the value type matches what you expect
81+
3. **Range Validation**: Ensure numeric values are within valid ranges
82+
4. **Return False on Error**: Return `False` if a setting cannot be applied
83+
5. **Persist Settings**: Consider saving settings to non-volatile storage
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
"""SinricPro Device Settings Example - Handle device-level configuration."""
2+
import asyncio
3+
import os
4+
from typing import Any
5+
6+
from sinricpro import SinricPro, SinricProBlinds, SinricProConfig
7+
8+
# Device ID from SinricPro portal
9+
DEVICE_ID = "YOUR_DEVICE_ID_HERE" # Replace with your device ID
10+
11+
# Credentials from SinricPro portal
12+
APP_KEY = os.getenv("SINRICPRO_APP_KEY", "YOUR_APP_KEY_HERE")
13+
APP_SECRET = os.getenv("SINRICPRO_APP_SECRET", "YOUR_APP_SECRET_HERE")
14+
15+
# Device state
16+
power_state = False
17+
position = 0 # 0-100
18+
19+
# Device-specific settings
20+
device_settings = {
21+
"id_tilt": 50, # Tilt angle (0-100)
22+
"id_direction": "up", # Movement direction preference
23+
"id_speed": "normal", # Movement speed: slow, normal, fast
24+
"id_auto_close": False, # Auto-close after timeout
25+
"id_close_timeout": 300, # Auto-close timeout in seconds
26+
}
27+
28+
29+
async def on_power_state(state: bool) -> bool:
30+
"""Handle power state change requests."""
31+
global power_state
32+
print(f"\n[Callback] Power: {'ON' if state else 'OFF'}")
33+
power_state = state
34+
return True
35+
36+
37+
async def on_device_setting(setting_id: str, value: Any) -> bool:
38+
"""
39+
Handle device-level setting changes.
40+
41+
Device settings are configuration values specific to this device,
42+
such as tilt angle, movement direction, speed preferences, etc.
43+
44+
Args:
45+
setting_id: The setting identifier (e.g., "tilt", "direction")
46+
value: The new value for the setting (can be int, float, bool, or string)
47+
48+
Returns:
49+
True if the setting was applied successfully, False otherwise
50+
"""
51+
print(f"\n[Device Setting] {setting_id} = {value} (type: {type(value).__name__})")
52+
53+
# Handle tilt setting
54+
if setting_id == "id_tilt":
55+
if isinstance(value, (int, float)) and 0 <= value <= 100:
56+
device_settings["tilt"] = int(value)
57+
print(f" Tilt angle set to {int(value)}%")
58+
# TODO: Apply tilt to physical device
59+
# set_blinds_tilt(int(value))
60+
return True
61+
else:
62+
print(f" Invalid tilt value: {value} (must be 0-100)")
63+
return False
64+
65+
# Handle direction setting
66+
elif setting_id == "id_direction":
67+
valid_directions = ["up", "down"]
68+
if isinstance(value, str) and value.lower() in valid_directions:
69+
device_settings["direction"] = value.lower()
70+
print(f" Direction preference set to '{value.lower()}'")
71+
return True
72+
else:
73+
print(f" Invalid direction value: {value} (must be 'up' or 'down')")
74+
return False
75+
76+
# Handle speed setting
77+
elif setting_id == "id_speed":
78+
valid_speeds = ["slow", "normal", "fast"]
79+
if isinstance(value, str) and value.lower() in valid_speeds:
80+
device_settings["speed"] = value.lower()
81+
print(f" Movement speed set to '{value.lower()}'")
82+
# TODO: Apply speed to physical device
83+
# set_motor_speed(value.lower())
84+
return True
85+
else:
86+
print(f" Invalid speed value: {value} (must be 'slow', 'normal', or 'fast')")
87+
return False
88+
89+
# Handle auto_close setting
90+
elif setting_id == "id_auto_close":
91+
if isinstance(value, bool):
92+
device_settings["auto_close"] = value
93+
print(f" Auto-close {'enabled' if value else 'disabled'}")
94+
return True
95+
else:
96+
print(f" Invalid auto_close value: {value} (must be boolean)")
97+
return False
98+
99+
# Handle close_timeout setting
100+
elif setting_id == "id_close_timeout":
101+
if isinstance(value, (int, float)) and 60 <= value <= 3600:
102+
device_settings["close_timeout"] = int(value)
103+
print(f" Close timeout set to {int(value)} seconds")
104+
return True
105+
else:
106+
print(f" Invalid close_timeout value: {value} (must be 60-3600)")
107+
return False
108+
109+
else:
110+
print(f" Unknown setting: {setting_id}")
111+
return False
112+
113+
114+
async def main() -> None:
115+
# Get SinricPro instance
116+
sinric_pro = SinricPro.get_instance()
117+
118+
# Create a blinds device
119+
blinds = SinricProBlinds(DEVICE_ID)
120+
121+
# Register device callbacks
122+
blinds.on_power_state(on_power_state)
123+
124+
# Register device-level setting callback
125+
# This handles settings specific to this device
126+
blinds.on_setting(on_device_setting)
127+
128+
# Add device to SinricPro
129+
sinric_pro.add(blinds)
130+
131+
# Example function to demonstrate sending device setting events
132+
async def send_example_setting():
133+
"""Send an example device setting event after connection."""
134+
await asyncio.sleep(5) # Wait for connection to stabilize
135+
print("\n[Example] Sending device setting event...")
136+
sent = await blinds.send_setting_event("id_tilt", 75)
137+
print(f" Device setting event sent: {sent}")
138+
139+
# Configure connection
140+
config = SinricProConfig(app_key=APP_KEY, app_secret=APP_SECRET)
141+
142+
try:
143+
print("=" * 60)
144+
print("SinricPro Device Settings Example")
145+
print("=" * 60)
146+
print("\nConnecting to SinricPro...")
147+
await sinric_pro.begin(config)
148+
print("Connected!")
149+
150+
print("\n" + "=" * 60)
151+
print("Device Settings vs Module Settings:")
152+
print("=" * 60)
153+
print(" Device Settings: Configuration for THIS specific device")
154+
print(" - Receive via: device.on_setting(callback)")
155+
print(" - Send via: device.send_setting_event(setting_id, value)")
156+
print(" - Examples: Tilt angle, speed, direction, auto-close")
157+
print(" - Callback receives: (setting_id, value)")
158+
print("")
159+
print(" Module Settings: Configuration for the module/board")
160+
print(" - Receive via: sinric_pro.on_set_setting(callback)")
161+
print(" - Send via: sinric_pro.send_setting_event(setting_id, value)")
162+
print(" - Examples: WiFi retry count, log level")
163+
164+
print("\n" + "=" * 60)
165+
print("Current Device Settings:")
166+
print("=" * 60)
167+
for key, value in device_settings.items():
168+
print(f" {key}: {value}")
169+
170+
print("\n" + "=" * 60)
171+
print("Voice Commands:")
172+
print("=" * 60)
173+
print(" 'Alexa, turn on [device name]'")
174+
print(" 'Alexa, set [device name] to 50 percent'")
175+
print(" (Device settings are configured via SinricPro portal)")
176+
177+
print("\n" + "=" * 60)
178+
print("Press Ctrl+C to exit")
179+
print("=" * 60)
180+
181+
# Start the example setting event task
182+
#asyncio.create_task(send_example_setting())
183+
184+
while True:
185+
await asyncio.sleep(1)
186+
187+
except KeyboardInterrupt:
188+
print("\n\nShutting down...")
189+
except Exception as e:
190+
print(f"\nError: {e}")
191+
import traceback
192+
traceback.print_exc()
193+
finally:
194+
await sinric_pro.stop()
195+
print("Disconnected.")
196+
197+
198+
if __name__ == "__main__":
199+
asyncio.run(main())

examples/settings/module/README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Module Settings Example
2+
3+
This example demonstrates how to handle **module-level settings** in SinricPro.
4+
5+
## Module Settings vs Device Settings
6+
7+
SinricPro supports two types of settings:
8+
9+
### Module Settings
10+
- Configuration for the **module (dev board)** itself
11+
- Registered via: `sinric_pro.on_set_setting(callback)`
12+
- Examples: WiFi retry count, log level, heartbeat interval
13+
- Callback signature: `async def callback(setting_id: str, value: Any) -> bool`
14+
15+
### Device Settings
16+
- Configuration for **individual devices**
17+
- Registered via: `device.on_setting(callback)`
18+
- Examples: Device-specific modes, thresholds, tilt settings
19+
- Callback signature: `async def callback(setting_id: str, value: Any) -> bool`
20+
21+
## Usage
22+
23+
```python
24+
from sinricpro import SinricPro
25+
26+
sinric_pro = SinricPro.get_instance()
27+
28+
# Register module-level setting callback
29+
async def on_module_setting(setting_id: str, value: Any) -> bool:
30+
if setting_id == "wifi_retry_count":
31+
set_wifi_retry_count(value)
32+
return True
33+
return False
34+
35+
sinric_pro.on_set_setting(on_module_setting)
36+
```
37+
38+
## Running the Example
39+
40+
1. Replace `YOUR_DEVICE_ID_HERE` with your actual device ID
41+
2. Set environment variables or replace credentials:
42+
```bash
43+
export SINRICPRO_APP_KEY="your-app-key"
44+
export SINRICPRO_APP_SECRET="your-app-secret"
45+
```
46+
3. Run the example:
47+
```bash
48+
python modulesettings_example.py
49+
```
50+
51+
## Setting Value Types
52+
53+
Module settings can have different value types:
54+
- **Integer**: `wifi_retry_count`, `heartbeat_interval`
55+
- **String**: `log_level`
56+
- **Boolean**: `debug_mode`
57+
- **Float**: `temperature_offset`
58+
59+
Always validate the value type and range before applying settings.

0 commit comments

Comments
 (0)