Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ yarn-error.log*


.claude.md
claude.md
claude.md
notes-to-aruna.txt
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## [5.2.0]

feat: Send a device setting event to SinricPro

## [5.1.0]

feat: Implemented example applications for various device types including Camera, PowerSensor, Device Settings, Module Settings, and Temperature Sensor.
Expand Down
14 changes: 12 additions & 2 deletions examples/settings/device/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ async function main() {
return false;
});

// Example: Send a device setting event to SinricPro
// This can be used to report setting changes made locally (e.g., via physical button)
// setTimeout(async () => {
// console.log('\n[Example] Sending device setting event...');
// const sent = await myBlinds.sendSettingEvent('id_tilt', 75);
// console.log(` Setting event sent: ${sent}`);
// }, 5000);

// Add device to SinricPro
SinricPro.add(myBlinds);

Expand All @@ -67,12 +75,14 @@ async function main() {
console.log('Device Settings vs Module Settings:');
console.log('='.repeat(60));
console.log(' Device Settings: Configuration for THIS specific device');
console.log(' - Registered via: device.onSetting(callback)');
console.log(' - Receive via: device.onSetting(callback)');
console.log(' - Send via: device.sendSettingEvent(settingId, value)');
console.log(' - Examples: Tilt angle');
console.log(' - Callback receives: (deviceId, settingId, value)');
console.log('');
console.log(' Module Settings: Configuration for the module/board');
console.log(' - Registered via: SinricPro.onSetSetting(callback)');
console.log(' - Receive via: SinricPro.onSetSetting(callback)');
console.log(' - Send via: SinricPro.sendSettingEvent(settingId, value)');
console.log(' - Examples: WiFi retry count, log level');

console.log('\n' + '='.repeat(60));
Expand Down
15 changes: 14 additions & 1 deletion examples/settings/module/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ async function main() {

SinricPro.onConnected(() => {
console.log('\nConnected to SinricPro!');

// Example: Send a module setting event to SinricPro
// This can be used to report module-level settings changes
// setTimeout(async () => {
// console.log('\n[Example] Sending module setting event...');
// const sent = await SinricPro.sendSettingEvent('if_wifiretrycount', 5);
// console.log(` Module setting event sent: ${sent}`);
// }, 5000);
});

// Initialize SinricPro
Expand All @@ -73,9 +81,14 @@ async function main() {
console.log('Module Settings vs Device Settings:');
console.log('='.repeat(60));
console.log(' Module Settings: Configuration for the module/board itself');
console.log(' - Registered via: SinricPro.onSetSetting(callback)');
console.log(' - Receive via: SinricPro.onSetSetting(callback)');
console.log(' - Send via: SinricPro.sendSettingEvent(settingId, value)');
console.log(' - Examples: WiFi retry count');
console.log('');
console.log(' Device Settings: Configuration for individual devices');
console.log(' - Receive via: device.onSetting(callback)');
console.log(' - Send via: device.sendSettingEvent(settingId, value)');
console.log('');

console.log('\n' + '='.repeat(60));
console.log('Current Module Configuration:');
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sinricpro",
"version": "5.1.0",
"version": "5.2.0",
"description": "Official SinricPro SDK for Node.js and TypeScript - Control IoT devices with Alexa and Google Home",
"main": "dist/index.js",
"exports": {
Expand Down
16 changes: 16 additions & 0 deletions src/capabilities/SettingController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
*/

import { SinricProDevice } from '../core/SinricProDevice';
import { EventLimiter } from '../core/EventLimiter';
import type { SinricProRequest } from '../core/types';
import { EVENT_LIMIT_STATE, PHYSICAL_INTERACTION } from '../core/types';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Constructor<T = object> = new (...args: any[]) => T;
Expand All @@ -16,11 +18,13 @@ export type SettingCallback = (

export interface ISettingController {
onSetting(callback: SettingCallback): void;
sendSettingEvent(settingId: string, value: unknown, cause?: string): Promise<boolean>;
}

export function SettingController<T extends Constructor<SinricProDevice>>(Base: T) {
return class extends Base implements ISettingController {
private settingCallback: SettingCallback | null = null;
private settingEventLimiter: EventLimiter = new EventLimiter(EVENT_LIMIT_STATE);

// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(...args: any[]) {
Expand All @@ -32,6 +36,18 @@ export function SettingController<T extends Constructor<SinricProDevice>>(Base:
this.settingCallback = callback;
}

async sendSettingEvent(
settingId: string,
value: unknown,
cause: string = PHYSICAL_INTERACTION
): Promise<boolean> {
if (this.settingEventLimiter.isLimited()) {
return false;
}

return this.sendEvent('setSetting', { id: settingId, value }, cause);
}

private async handleSettingRequest(request: SinricProRequest): Promise<boolean> {
if (request.action !== 'setSetting' || !this.settingCallback) {
return false;
Expand Down
65 changes: 64 additions & 1 deletion src/core/SinricPro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import type {
PongCallback,
ModuleSettingCallback,
} from './types';
import { SINRICPRO_SERVER_URL } from './types';
import { SINRICPRO_SERVER_URL, EVENT_LIMIT_STATE, PHYSICAL_INTERACTION } from './types';
import { EventLimiter } from './EventLimiter';

// Internal config type with serverUrl
interface InternalConfig extends Required<SinricProConfig> {
Expand All @@ -37,6 +38,7 @@ export class SinricPro extends EventEmitter implements ISinricPro {
private isInitialized: boolean = false;
private processingInterval: NodeJS.Timeout | null = null;
private moduleSettingCallback: ModuleSettingCallback | null = null;
private settingEventLimiter: EventLimiter = new EventLimiter(EVENT_LIMIT_STATE);

private constructor() {
super();
Expand Down Expand Up @@ -216,6 +218,67 @@ export class SinricPro extends EventEmitter implements ISinricPro {
this.moduleSettingCallback = callback;
}

/**
* Send a module-level setting event to SinricPro server
*
* Module settings are configuration values for the module (dev board) itself.
* Use this to report setting changes like WiFi configuration, logging level,
* or other module-wide settings.
*
* @param settingId - The setting identifier
* @param value - The setting value (can be any JSON-serializable type)
* @param cause - (optional) Reason for the event (default: 'PHYSICAL_INTERACTION')
* @returns Promise<boolean> - true if event was sent, false if rate limited
* @example
* ```typescript
* await SinricPro.sendSettingEvent('wifi_retry_count', 5);
* await SinricPro.sendSettingEvent('debug_mode', true);
* ```
*/
async sendSettingEvent(
settingId: string,
value: unknown,
cause: string = PHYSICAL_INTERACTION
): Promise<boolean> {
if (this.settingEventLimiter.isLimited()) {
return false;
}

if (!this.isConnected()) {
SinricProSdkLogger.error('Cannot send setting event: Not connected to SinricPro');
return false;
}

const eventMessage: SinricProMessage = {
header: {
payloadVersion: 2,
signatureVersion: 1,
},
payload: {
action: 'setSetting',
replyToken: this.generateMessageId(),
type: 'event' as MessageType,
createdAt: this.getTimestamp(),
cause: { type: cause },
scope: 'module',
value: { id: settingId, value },
},
};

try {
await this.sendMessage(eventMessage);
SinricProSdkLogger.debug(`Module setting event sent: ${settingId}`, value);
return true;
} catch (error) {
SinricProSdkLogger.error(`Failed to send module setting event ${settingId}:`, error);
return false;
}
}

private generateMessageId(): string {
return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
}

/**
* Stop the SinricPro SDK and disconnect from the server
* @example
Expand Down
Loading