smart_message 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +31 -1
- data/Gemfile.lock +6 -1
- data/README.md +103 -21
- data/docs/README.md +5 -4
- data/docs/architecture.md +41 -8
- data/docs/dispatcher.md +52 -16
- data/docs/getting-started.md +64 -2
- data/docs/message_processing.md +423 -0
- data/docs/proc_handlers_summary.md +247 -0
- data/docs/properties.md +471 -0
- data/docs/transports.md +202 -8
- data/examples/04_redis_smart_home_iot.rb +649 -0
- data/examples/05_proc_handlers.rb +181 -0
- data/examples/README.md +118 -3
- data/examples/smart_home_iot_dataflow.md +257 -0
- data/lib/smart_message/base.rb +108 -4
- data/lib/smart_message/dispatcher.rb +22 -6
- data/lib/smart_message/property_descriptions.rb +41 -0
- data/lib/smart_message/transport/redis_transport.rb +190 -0
- data/lib/smart_message/transport/registry.rb +1 -0
- data/lib/smart_message/transport.rb +1 -0
- data/lib/smart_message/version.rb +1 -1
- data/smart_message.gemspec +1 -0
- metadata +23 -1
@@ -0,0 +1,181 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# examples/05_proc_handlers.rb
|
3
|
+
#
|
4
|
+
# Proc and Block Handler Example
|
5
|
+
#
|
6
|
+
# This example demonstrates the new proc and block handler functionality
|
7
|
+
# in SmartMessage, showing different ways to subscribe to messages beyond
|
8
|
+
# the traditional self.process method.
|
9
|
+
|
10
|
+
require_relative '../lib/smart_message'
|
11
|
+
|
12
|
+
puts "=== SmartMessage Proc and Block Handler Example ==="
|
13
|
+
puts
|
14
|
+
|
15
|
+
# Define a simple notification message
|
16
|
+
class NotificationMessage < SmartMessage::Base
|
17
|
+
property :type # 'info', 'warning', 'error'
|
18
|
+
property :title
|
19
|
+
property :message
|
20
|
+
property :user_id
|
21
|
+
property :timestamp
|
22
|
+
|
23
|
+
config do
|
24
|
+
transport SmartMessage::Transport::StdoutTransport.new(loopback: true)
|
25
|
+
serializer SmartMessage::Serializer::JSON.new
|
26
|
+
end
|
27
|
+
|
28
|
+
# Default handler
|
29
|
+
def self.process(message_header, message_payload)
|
30
|
+
data = JSON.parse(message_payload)
|
31
|
+
icon = case data['type']
|
32
|
+
when 'info' then 'ℹ️'
|
33
|
+
when 'warning' then '⚠️'
|
34
|
+
when 'error' then '🚨'
|
35
|
+
else '📢'
|
36
|
+
end
|
37
|
+
puts "#{icon} [DEFAULT] #{data['title']}: #{data['message']}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
puts "🚀 Setting up different types of message handlers"
|
42
|
+
puts
|
43
|
+
|
44
|
+
# 1. Default handler (traditional way)
|
45
|
+
puts "1️⃣ Default handler subscription:"
|
46
|
+
default_id = NotificationMessage.subscribe
|
47
|
+
puts " Subscribed with ID: #{default_id}"
|
48
|
+
puts
|
49
|
+
|
50
|
+
# 2. Block handler
|
51
|
+
puts "2️⃣ Block handler subscription:"
|
52
|
+
block_id = NotificationMessage.subscribe do |header, payload|
|
53
|
+
data = JSON.parse(payload)
|
54
|
+
if data['type'] == 'error'
|
55
|
+
puts "🔥 [BLOCK] Critical error logged: #{data['title']}"
|
56
|
+
# Could send to error tracking service here
|
57
|
+
end
|
58
|
+
end
|
59
|
+
puts " Subscribed with ID: #{block_id}"
|
60
|
+
puts
|
61
|
+
|
62
|
+
# 3. Proc handler
|
63
|
+
puts "3️⃣ Proc handler subscription:"
|
64
|
+
audit_logger = proc do |header, payload|
|
65
|
+
data = JSON.parse(payload)
|
66
|
+
timestamp = header.published_at.strftime('%Y-%m-%d %H:%M:%S')
|
67
|
+
puts "📝 [AUDIT] #{timestamp} - User #{data['user_id']}: #{data['type'].upcase}"
|
68
|
+
end
|
69
|
+
|
70
|
+
proc_id = NotificationMessage.subscribe(audit_logger)
|
71
|
+
puts " Subscribed with ID: #{proc_id}"
|
72
|
+
puts
|
73
|
+
|
74
|
+
# 4. Lambda handler
|
75
|
+
puts "4️⃣ Lambda handler subscription:"
|
76
|
+
warning_filter = lambda do |header, payload|
|
77
|
+
data = JSON.parse(payload)
|
78
|
+
if data['type'] == 'warning'
|
79
|
+
puts "⚡ [LAMBDA] Warning for user #{data['user_id']}: #{data['message']}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
lambda_id = NotificationMessage.subscribe(warning_filter)
|
84
|
+
puts " Subscribed with ID: #{lambda_id}"
|
85
|
+
puts
|
86
|
+
|
87
|
+
# 5. Method handler (traditional, but shown for comparison)
|
88
|
+
puts "5️⃣ Method handler subscription:"
|
89
|
+
class NotificationService
|
90
|
+
def self.handle_notifications(header, payload)
|
91
|
+
data = JSON.parse(payload)
|
92
|
+
puts "🏢 [SERVICE] Processing #{data['type']} notification for user #{data['user_id']}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
method_id = NotificationMessage.subscribe("NotificationService.handle_notifications")
|
97
|
+
puts " Subscribed with ID: #{method_id}"
|
98
|
+
puts
|
99
|
+
|
100
|
+
puts "=" * 60
|
101
|
+
puts "📡 Publishing test notifications (watch the different handlers respond!)"
|
102
|
+
puts "=" * 60
|
103
|
+
puts
|
104
|
+
|
105
|
+
# Test the handlers with different notification types
|
106
|
+
notifications = [
|
107
|
+
{
|
108
|
+
type: 'info',
|
109
|
+
title: 'Welcome',
|
110
|
+
message: 'Welcome to the system!',
|
111
|
+
user_id: 'user123',
|
112
|
+
timestamp: Time.now.iso8601
|
113
|
+
},
|
114
|
+
{
|
115
|
+
type: 'warning',
|
116
|
+
title: 'Low Disk Space',
|
117
|
+
message: 'Your disk space is running low',
|
118
|
+
user_id: 'user456',
|
119
|
+
timestamp: Time.now.iso8601
|
120
|
+
},
|
121
|
+
{
|
122
|
+
type: 'error',
|
123
|
+
title: 'Database Connection Failed',
|
124
|
+
message: 'Unable to connect to the database',
|
125
|
+
user_id: 'system',
|
126
|
+
timestamp: Time.now.iso8601
|
127
|
+
}
|
128
|
+
]
|
129
|
+
|
130
|
+
notifications.each_with_index do |notification_data, index|
|
131
|
+
puts "\n📤 Publishing notification #{index + 1}: #{notification_data[:title]}"
|
132
|
+
|
133
|
+
notification = NotificationMessage.new(**notification_data)
|
134
|
+
notification.publish
|
135
|
+
|
136
|
+
# Give time for all handlers to process
|
137
|
+
sleep(0.5)
|
138
|
+
|
139
|
+
puts " ✅ All handlers processed the #{notification_data[:type]} notification"
|
140
|
+
end
|
141
|
+
|
142
|
+
puts "\n" + "=" * 60
|
143
|
+
puts "🔧 Demonstrating handler management"
|
144
|
+
puts "=" * 60
|
145
|
+
|
146
|
+
# Show how to unsubscribe handlers
|
147
|
+
puts "\n🗑️ Unsubscribing the block handler..."
|
148
|
+
NotificationMessage.unsubscribe(block_id)
|
149
|
+
puts " Block handler removed"
|
150
|
+
|
151
|
+
puts "\n📤 Publishing another error notification (block handler won't respond):"
|
152
|
+
error_notification = NotificationMessage.new(
|
153
|
+
type: 'error',
|
154
|
+
title: 'Another Error',
|
155
|
+
message: 'This error won\'t trigger the block handler',
|
156
|
+
user_id: 'test_user',
|
157
|
+
timestamp: Time.now.iso8601
|
158
|
+
)
|
159
|
+
|
160
|
+
error_notification.publish
|
161
|
+
sleep(0.5)
|
162
|
+
|
163
|
+
puts "\n" + "=" * 60
|
164
|
+
puts "✨ Example completed!"
|
165
|
+
puts "=" * 60
|
166
|
+
|
167
|
+
puts "\nThis example demonstrated:"
|
168
|
+
puts "• ✅ Default self.process method (traditional)"
|
169
|
+
puts "• ✅ Block handlers with subscribe { |h,p| ... } (NEW!)"
|
170
|
+
puts "• ✅ Proc handlers with subscribe(proc { ... }) (NEW!)"
|
171
|
+
puts "• ✅ Lambda handlers with subscribe(lambda { ... }) (NEW!)"
|
172
|
+
puts "• ✅ Method handlers with subscribe('Class.method') (traditional)"
|
173
|
+
puts "• ✅ Handler unsubscription and management"
|
174
|
+
puts "\nEach handler type has its own use cases:"
|
175
|
+
puts "• 🎯 Default: Simple built-in processing"
|
176
|
+
puts "• 🔧 Blocks: Inline logic for specific subscriptions"
|
177
|
+
puts "• 🔄 Procs: Reusable handlers across message types"
|
178
|
+
puts "• ⚡ Lambdas: Strict argument checking and functional style"
|
179
|
+
puts "• 🏢 Methods: Organized, testable business logic"
|
180
|
+
|
181
|
+
puts "\n🎉 Choose the handler type that best fits your needs!"
|
data/examples/README.md
CHANGED
@@ -11,6 +11,8 @@ cd examples
|
|
11
11
|
ruby 01_point_to_point_orders.rb
|
12
12
|
ruby 02_publish_subscribe_events.rb
|
13
13
|
ruby 03_many_to_many_chat.rb
|
14
|
+
ruby 04_redis_smart_home_iot.rb
|
15
|
+
ruby 05_proc_handlers.rb
|
14
16
|
```
|
15
17
|
|
16
18
|
## Examples Overview
|
@@ -97,6 +99,105 @@ ruby 03_many_to_many_chat.rb
|
|
97
99
|
- Service capabilities and discovery
|
98
100
|
- Complex subscription management
|
99
101
|
|
102
|
+
---
|
103
|
+
|
104
|
+
### 4. Redis Transport IoT Example (Production Messaging)
|
105
|
+
**File:** `04_redis_smart_home_iot.rb`
|
106
|
+
|
107
|
+
**Scenario:** Smart home IoT dashboard with multiple device types communicating through Redis pub/sub channels, demonstrating production-ready messaging patterns.
|
108
|
+
|
109
|
+
**Key Features:**
|
110
|
+
- Real Redis pub/sub transport (falls back to memory if Redis unavailable)
|
111
|
+
- Multiple device types with realistic sensor data
|
112
|
+
- Automatic Redis channel routing using message class names
|
113
|
+
- Real-time monitoring and alerting system
|
114
|
+
- Production-ready error handling and reconnection
|
115
|
+
|
116
|
+
**Messages Used:**
|
117
|
+
- `SensorDataMessage` - IoT device sensor readings and status
|
118
|
+
- `DeviceCommandMessage` - Commands sent to control devices
|
119
|
+
- `AlertMessage` - Critical notifications and warnings
|
120
|
+
- `DashboardStatusMessage` - System-wide status updates
|
121
|
+
|
122
|
+
**Services:**
|
123
|
+
- `SmartThermostat` - Temperature monitoring and control
|
124
|
+
- `SecurityCamera` - Motion detection and recording
|
125
|
+
- `SmartDoorLock` - Access control and status monitoring
|
126
|
+
- `IoTDashboard` - Centralized monitoring and status aggregation
|
127
|
+
|
128
|
+
**What You'll Learn:**
|
129
|
+
- Production Redis transport configuration and usage
|
130
|
+
- Automatic Redis channel routing (each message type → separate channel)
|
131
|
+
- IoT device simulation and real-time data streaming
|
132
|
+
- Event-driven alert systems
|
133
|
+
- Scalable pub/sub architecture for distributed systems
|
134
|
+
- Error handling and graceful fallbacks
|
135
|
+
|
136
|
+
**Redis Channels Created:**
|
137
|
+
- `SensorDataMessage` - Device sensor readings
|
138
|
+
- `DeviceCommandMessage` - Device control commands
|
139
|
+
- `AlertMessage` - System alerts and notifications
|
140
|
+
- `DashboardStatusMessage` - Dashboard status updates
|
141
|
+
|
142
|
+
---
|
143
|
+
|
144
|
+
### 5. Proc and Block Handler Example (Flexible Message Processing)
|
145
|
+
**File:** `05_proc_handlers.rb`
|
146
|
+
|
147
|
+
**Scenario:** Notification system demonstrating all available message handler types in SmartMessage, showcasing the flexibility of the new proc and block subscription patterns.
|
148
|
+
|
149
|
+
**Key Features:**
|
150
|
+
- Multiple handler types in a single application
|
151
|
+
- Default method handlers alongside new proc/block handlers
|
152
|
+
- Dynamic handler management (subscription and unsubscription)
|
153
|
+
- Practical comparison of different handler approaches
|
154
|
+
|
155
|
+
**Messages Used:**
|
156
|
+
- `NotificationMessage` - System notifications with type, title, message, and user info
|
157
|
+
|
158
|
+
**Handler Types Demonstrated:**
|
159
|
+
- `Default Handler` - Traditional `self.process` method
|
160
|
+
- `Block Handler` - Inline logic using `subscribe do |h,p|...end`
|
161
|
+
- `Proc Handler` - Reusable proc objects for cross-cutting concerns
|
162
|
+
- `Lambda Handler` - Strict parameter validation with functional style
|
163
|
+
- `Method Handler` - Organized service class methods
|
164
|
+
|
165
|
+
**What You'll Learn:**
|
166
|
+
- How to choose the right handler type for different use cases
|
167
|
+
- Block handlers for simple, subscription-specific logic
|
168
|
+
- Proc handlers for reusable cross-message functionality
|
169
|
+
- Lambda handlers for strict functional programming patterns
|
170
|
+
- Handler lifecycle management and cleanup
|
171
|
+
- Performance characteristics of different handler types
|
172
|
+
|
173
|
+
**Handler Patterns Shown:**
|
174
|
+
```ruby
|
175
|
+
# Default handler
|
176
|
+
class NotificationMessage < SmartMessage::Base
|
177
|
+
def self.process(header, payload)
|
178
|
+
# Built-in processing
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# Block handler - inline logic
|
183
|
+
NotificationMessage.subscribe do |header, payload|
|
184
|
+
# Simple, specific processing
|
185
|
+
end
|
186
|
+
|
187
|
+
# Proc handler - reusable across message types
|
188
|
+
audit_logger = proc { |header, payload| log_audit(payload) }
|
189
|
+
NotificationMessage.subscribe(audit_logger)
|
190
|
+
|
191
|
+
# Method handler - organized service logic
|
192
|
+
NotificationMessage.subscribe("NotificationService.handle")
|
193
|
+
```
|
194
|
+
|
195
|
+
**Benefits Demonstrated:**
|
196
|
+
- **Flexibility**: Multiple ways to handle the same message type
|
197
|
+
- **Reusability**: Proc handlers can be shared across message classes
|
198
|
+
- **Maintainability**: Choose the right abstraction level for each need
|
199
|
+
- **Performance**: Understand overhead of different handler approaches
|
200
|
+
|
100
201
|
## Message Patterns Demonstrated
|
101
202
|
|
102
203
|
### Request-Response Pattern
|
@@ -129,7 +230,7 @@ alice.leave_room('general') # Stop receiving messages
|
|
129
230
|
|
130
231
|
## Transport Configurations
|
131
232
|
|
132
|
-
|
233
|
+
Most examples use `StdoutTransport` with loopback enabled for demonstration purposes:
|
133
234
|
|
134
235
|
```ruby
|
135
236
|
config do
|
@@ -138,8 +239,21 @@ config do
|
|
138
239
|
end
|
139
240
|
```
|
140
241
|
|
242
|
+
**Exception:** The IoT example (`04_redis_smart_home_iot.rb`) uses real Redis transport:
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
config do
|
246
|
+
transport SmartMessage::Transport.create(:redis,
|
247
|
+
url: 'redis://localhost:6379',
|
248
|
+
db: 1,
|
249
|
+
auto_subscribe: true
|
250
|
+
)
|
251
|
+
serializer SmartMessage::Serializer::JSON.new
|
252
|
+
end
|
253
|
+
```
|
254
|
+
|
141
255
|
**For Production Use:**
|
142
|
-
-
|
256
|
+
- Use production transports like Redis (see example #4), RabbitMQ, or Kafka
|
143
257
|
- Configure appropriate serializers for your data needs
|
144
258
|
- Add proper error handling and logging
|
145
259
|
- Implement monitoring and metrics
|
@@ -242,6 +356,7 @@ Each example includes:
|
|
242
356
|
ruby examples/01_point_to_point_orders.rb
|
243
357
|
ruby examples/02_publish_subscribe_events.rb
|
244
358
|
ruby examples/03_many_to_many_chat.rb
|
359
|
+
ruby examples/04_redis_smart_home_iot.rb # Requires Redis server
|
245
360
|
```
|
246
361
|
|
247
362
|
3. **Expected output:**
|
@@ -293,7 +408,7 @@ end
|
|
293
408
|
When adapting these examples for production:
|
294
409
|
|
295
410
|
1. **Transport Selection:**
|
296
|
-
- Use production message brokers (Redis, RabbitMQ, Kafka)
|
411
|
+
- Use production message brokers (Redis is built-in - see example #4, RabbitMQ, Kafka)
|
297
412
|
- Configure connection pooling and failover
|
298
413
|
- Implement proper error handling
|
299
414
|
|
@@ -0,0 +1,257 @@
|
|
1
|
+
# Smart Home IoT Data Flow - Redis Pub/Sub Transport
|
2
|
+
|
3
|
+
This document describes the data flow architecture for the Smart Home IoT example using SmartMessage's Redis transport. The system demonstrates how multiple IoT devices communicate through Redis pub/sub channels with targeted message routing.
|
4
|
+
|
5
|
+
## System Architecture Overview
|
6
|
+
|
7
|
+
The smart home system consists of three types of IoT devices and a central dashboard, all communicating through Redis pub/sub channels. Each message type uses its own Redis channel for efficient routing and scaling.
|
8
|
+
|
9
|
+
```mermaid
|
10
|
+
graph TB
|
11
|
+
%% IoT Devices
|
12
|
+
THERM["🌡️ Smart Thermostat<br/>THERM-001<br/>Living Room"]
|
13
|
+
CAM["📹 Security Camera<br/>CAM-001<br/>Front Door"]
|
14
|
+
LOCK["🚪 Smart Door Lock<br/>LOCK-001<br/>Main Entrance"]
|
15
|
+
DASH["📊 IoT Dashboard<br/>System Monitor"]
|
16
|
+
|
17
|
+
%% Redis Channels
|
18
|
+
subgraph REDIS ["Redis Pub/Sub Channels"]
|
19
|
+
SENSOR["SensorDataMessage<br/>Temperature, Motion, Status<br/>Battery Levels"]
|
20
|
+
COMMAND["DeviceCommandMessage<br/>set_temperature, start_recording<br/>lock/unlock, get_status"]
|
21
|
+
ALERT["AlertMessage<br/>Motion Detected, Battery Low<br/>High Temperature"]
|
22
|
+
STATUS["DashboardStatusMessage<br/>System Status, Device Counts<br/>Alert Summaries"]
|
23
|
+
end
|
24
|
+
|
25
|
+
%% Device Publishing
|
26
|
+
THERM -.->|"Temperature Data"| SENSOR
|
27
|
+
CAM -.->|"Motion Data"| SENSOR
|
28
|
+
LOCK -.->|"Lock Status"| SENSOR
|
29
|
+
CAM -.->|"Motion Alerts"| ALERT
|
30
|
+
THERM -.->|"High Temp Alerts"| ALERT
|
31
|
+
LOCK -.->|"Battery Low Alerts"| ALERT
|
32
|
+
DASH -.->|"System Status"| STATUS
|
33
|
+
DASH -.->|"Device Commands"| COMMAND
|
34
|
+
|
35
|
+
%% Device Subscribing
|
36
|
+
COMMAND -->|"set_temperature"| THERM
|
37
|
+
COMMAND -->|"start/stop recording"| CAM
|
38
|
+
COMMAND -->|"lock/unlock"| LOCK
|
39
|
+
SENSOR -->|"All sensor data"| DASH
|
40
|
+
COMMAND -->|"Command logging"| DASH
|
41
|
+
ALERT -->|"All alerts"| DASH
|
42
|
+
STATUS -->|"Status updates"| DASH
|
43
|
+
|
44
|
+
%% Styling
|
45
|
+
classDef deviceStyle fill:#ff6b6b,stroke:#c0392b,stroke-width:2px,color:#fff
|
46
|
+
classDef cameraStyle fill:#4ecdc4,stroke:#16a085,stroke-width:2px,color:#fff
|
47
|
+
classDef lockStyle fill:#ffe66d,stroke:#f39c12,stroke-width:2px,color:#333
|
48
|
+
classDef dashStyle fill:#a8e6cf,stroke:#27ae60,stroke-width:2px,color:#333
|
49
|
+
classDef redisStyle fill:#dc143c,stroke:#a00,stroke-width:2px,color:#fff
|
50
|
+
|
51
|
+
class THERM deviceStyle
|
52
|
+
class CAM cameraStyle
|
53
|
+
class LOCK lockStyle
|
54
|
+
class DASH dashStyle
|
55
|
+
class SENSOR,COMMAND,ALERT,STATUS redisStyle
|
56
|
+
```
|
57
|
+
|
58
|
+
## Message Flow Details
|
59
|
+
|
60
|
+
### 1. Sensor Data Flow
|
61
|
+
All IoT devices continuously publish sensor readings to the `SensorDataMessage` Redis channel:
|
62
|
+
|
63
|
+
- **🌡️ Thermostat**: Temperature readings, battery level
|
64
|
+
- **📹 Camera**: Motion detection status, battery level
|
65
|
+
- **🚪 Door Lock**: Lock/unlock status, battery level
|
66
|
+
|
67
|
+
**Example SensorDataMessage:**
|
68
|
+
```json
|
69
|
+
{
|
70
|
+
"device_id": "THERM-001",
|
71
|
+
"device_type": "thermostat",
|
72
|
+
"location": "living_room",
|
73
|
+
"sensor_type": "temperature",
|
74
|
+
"value": 22.5,
|
75
|
+
"unit": "celsius",
|
76
|
+
"timestamp": "2025-08-18T10:30:00Z",
|
77
|
+
"battery_level": 85.2
|
78
|
+
}
|
79
|
+
```
|
80
|
+
|
81
|
+
### 2. Device Command Flow
|
82
|
+
The dashboard and external systems send commands to specific devices via the `DeviceCommandMessage` channel:
|
83
|
+
|
84
|
+
```mermaid
|
85
|
+
sequenceDiagram
|
86
|
+
participant App as Mobile App
|
87
|
+
participant Redis as Redis Channel
|
88
|
+
participant Therm as Thermostat
|
89
|
+
participant Cam as Camera
|
90
|
+
participant Lock as Door Lock
|
91
|
+
|
92
|
+
App->>Redis: DeviceCommandMessage<br/>device_id THERM-001 set_temperature
|
93
|
+
Redis->>Therm: ✅ Processes THERM prefix match
|
94
|
+
Redis->>Cam: ❌ Ignores not CAM prefix
|
95
|
+
Redis->>Lock: ❌ Ignores not LOCK prefix
|
96
|
+
|
97
|
+
App->>Redis: DeviceCommandMessage<br/>device_id CAM-001 start_recording
|
98
|
+
Redis->>Therm: ❌ Ignores not THERM prefix
|
99
|
+
Redis->>Cam: ✅ Processes CAM prefix match
|
100
|
+
Redis->>Lock: ❌ Ignores not LOCK prefix
|
101
|
+
```
|
102
|
+
|
103
|
+
**Device Command Filtering Rules:**
|
104
|
+
- **THERM-*** devices: Accept `set_temperature`, `get_status`
|
105
|
+
- **CAM-*** devices: Accept `start_recording`, `stop_recording`, `get_status`
|
106
|
+
- **LOCK-*** devices: Accept `lock`, `unlock`, `get_status`
|
107
|
+
|
108
|
+
### 3. Alert System Flow
|
109
|
+
Devices publish critical notifications to the `AlertMessage` channel when conditions are detected:
|
110
|
+
|
111
|
+
```mermaid
|
112
|
+
flowchart LR
|
113
|
+
subgraph Triggers
|
114
|
+
T1[High Temperature > 28°C]
|
115
|
+
T2[Motion Detected]
|
116
|
+
T3[Battery < 20%]
|
117
|
+
T4[Device Offline > 30s]
|
118
|
+
end
|
119
|
+
|
120
|
+
subgraph Devices
|
121
|
+
THERM2[🌡️ Thermostat]
|
122
|
+
CAM2[📹 Camera]
|
123
|
+
LOCK2[🚪 Door Lock]
|
124
|
+
end
|
125
|
+
|
126
|
+
subgraph AlertChannel [AlertMessage Channel]
|
127
|
+
A1[Motion Alert]
|
128
|
+
A2[High Temp Alert]
|
129
|
+
A3[Battery Low Alert]
|
130
|
+
A4[Device Offline Alert]
|
131
|
+
end
|
132
|
+
|
133
|
+
T1 --> THERM2 --> A2
|
134
|
+
T2 --> CAM2 --> A1
|
135
|
+
T3 --> LOCK2 --> A3
|
136
|
+
T4 --> A4
|
137
|
+
|
138
|
+
AlertChannel --> DASH2[📊 Dashboard]
|
139
|
+
```
|
140
|
+
|
141
|
+
### 4. Dashboard Status Flow
|
142
|
+
The dashboard aggregates all system data and publishes periodic status updates:
|
143
|
+
|
144
|
+
```mermaid
|
145
|
+
graph LR
|
146
|
+
subgraph DataCollection [Data Collection]
|
147
|
+
D1[Device Last Seen Times]
|
148
|
+
D2[Alert Counts]
|
149
|
+
D3[Battery Levels]
|
150
|
+
D4[System Health]
|
151
|
+
end
|
152
|
+
|
153
|
+
subgraph Processing [Status Processing]
|
154
|
+
P1[Count Active Devices<br/>last_seen < 30s]
|
155
|
+
P2[Count Recent Alerts<br/>last 5 minutes]
|
156
|
+
P3[Calculate Averages]
|
157
|
+
end
|
158
|
+
|
159
|
+
subgraph Output [Status Output]
|
160
|
+
O1[DashboardStatusMessage<br/>Every 10 seconds]
|
161
|
+
end
|
162
|
+
|
163
|
+
DataCollection --> Processing --> Output
|
164
|
+
```
|
165
|
+
|
166
|
+
## Channel-Based Architecture Benefits
|
167
|
+
|
168
|
+
### 1. **Efficient Message Routing**
|
169
|
+
Each message type uses its own Redis channel, preventing unnecessary message processing:
|
170
|
+
|
171
|
+
| Channel | Publishers | Subscribers | Purpose |
|
172
|
+
|---------|------------|-------------|---------|
|
173
|
+
| `SensorDataMessage` | All Devices | Dashboard | Real-time sensor readings |
|
174
|
+
| `DeviceCommandMessage` | Dashboard, Apps | All Devices | Device control commands |
|
175
|
+
| `AlertMessage` | All Devices | Dashboard | Critical notifications |
|
176
|
+
| `DashboardStatusMessage` | Dashboard | Dashboard, Apps | System status updates |
|
177
|
+
|
178
|
+
### 2. **Device-Specific Command Filtering**
|
179
|
+
Devices use prefix-based filtering to process only relevant commands:
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
# Example: Thermostat command filtering
|
183
|
+
def self.handle_command(message_header, message_payload)
|
184
|
+
command_data = JSON.parse(message_payload)
|
185
|
+
|
186
|
+
# Only process commands for thermostats
|
187
|
+
return unless command_data['device_id']&.start_with?('THERM-')
|
188
|
+
return unless ['set_temperature', 'get_status'].include?(command_data['command'])
|
189
|
+
|
190
|
+
# Process the command...
|
191
|
+
end
|
192
|
+
```
|
193
|
+
|
194
|
+
### 3. **Scalable Pub/Sub Pattern**
|
195
|
+
The architecture supports easy scaling:
|
196
|
+
|
197
|
+
- ✅ **Add new device types**: Just define new device ID prefixes
|
198
|
+
- ✅ **Add new message types**: Create new Redis channels as needed
|
199
|
+
- ✅ **Multiple instances**: Each device can have multiple instances
|
200
|
+
- ✅ **Load balancing**: Redis handles distribution automatically
|
201
|
+
|
202
|
+
## Running the Example
|
203
|
+
|
204
|
+
To see this data flow in action:
|
205
|
+
|
206
|
+
```bash
|
207
|
+
# Ensure Redis is running
|
208
|
+
redis-server
|
209
|
+
|
210
|
+
# Run the IoT example
|
211
|
+
cd examples
|
212
|
+
ruby 04_redis_smart_home_iot.rb
|
213
|
+
```
|
214
|
+
|
215
|
+
**What you'll observe:**
|
216
|
+
1. **Device initialization** and Redis connection setup
|
217
|
+
2. **Sensor data publishing** every 3-5 seconds per device
|
218
|
+
3. **Command routing** with device-specific responses
|
219
|
+
4. **Alert generation** when motion is detected or conditions change
|
220
|
+
5. **Dashboard status updates** every 10 seconds showing active device counts
|
221
|
+
|
222
|
+
## Redis Channel Monitoring
|
223
|
+
|
224
|
+
You can monitor the Redis channels directly:
|
225
|
+
|
226
|
+
```bash
|
227
|
+
# View active channels
|
228
|
+
redis-cli PUBSUB CHANNELS
|
229
|
+
|
230
|
+
# Monitor all channel activity
|
231
|
+
redis-cli MONITOR
|
232
|
+
|
233
|
+
# Subscribe to specific channels
|
234
|
+
redis-cli SUBSCRIBE SensorDataMessage
|
235
|
+
redis-cli SUBSCRIBE DeviceCommandMessage
|
236
|
+
redis-cli SUBSCRIBE AlertMessage
|
237
|
+
redis-cli SUBSCRIBE DashboardStatusMessage
|
238
|
+
```
|
239
|
+
|
240
|
+
## Key Design Patterns Demonstrated
|
241
|
+
|
242
|
+
### 1. **Message Class as Channel Name**
|
243
|
+
SmartMessage automatically uses the message class name as the Redis channel name, providing clean separation.
|
244
|
+
|
245
|
+
### 2. **Device ID-Based Routing**
|
246
|
+
Commands are filtered by device ID prefixes, ensuring only intended devices process commands.
|
247
|
+
|
248
|
+
### 3. **Centralized Monitoring**
|
249
|
+
The dashboard subscribes to all channels, providing comprehensive system visibility.
|
250
|
+
|
251
|
+
### 4. **Event-Driven Alerts**
|
252
|
+
Devices autonomously generate alerts based on sensor readings and conditions.
|
253
|
+
|
254
|
+
### 5. **Graceful Degradation**
|
255
|
+
System falls back to memory transport if Redis is unavailable, ensuring development continues.
|
256
|
+
|
257
|
+
This architecture demonstrates production-ready IoT messaging patterns using Redis pub/sub for efficient, scalable device communication.
|