-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfirebase_utils.py
More file actions
107 lines (89 loc) · 3.32 KB
/
Copy pathfirebase_utils.py
File metadata and controls
107 lines (89 loc) · 3.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# coding: utf-8
# Firebase reference helpers shared between action_devices and notifications.
# Extracted to a dedicated module to break the action_devices ↔ notifications
# cyclic import.
import logging
logger = logging.getLogger(__name__)
try:
from firebase_admin import db
FIREBASE_AVAILABLE = True
except ImportError:
FIREBASE_AVAILABLE = False
logger.warning("Firebase admin not available, using mock data for testing")
# Mock data for testing when Firebase is not available
MOCK_DEVICES = {
"test-light-1": {
"type": "action.devices.types.LIGHT",
"traits": ["action.devices.traits.OnOff", "action.devices.traits.Brightness"],
"name": {"name": "Test Light"},
"willReportState": True,
"attributes": {"colorModel": "rgb"},
"states": {"on": True, "brightness": 80, "online": True}
},
"test-switch-1": {
"type": "action.devices.types.SWITCH",
"traits": ["action.devices.traits.OnOff"],
"name": {"name": "Test Switch"},
"willReportState": True,
"states": {"on": False, "online": True}
}
}
class MockRef:
@staticmethod
def get():
return MOCK_DEVICES
@staticmethod
def child(path):
return MockChild(MOCK_DEVICES, path)
class MockChild:
def __init__(self, data, path):
self.data = data
self.path = path
def child(self, child_path):
return MockChild(self.data, self.path + '/' + child_path)
def get(self):
keys = self.path.split('/')
current = self.data
for key in keys:
if isinstance(current, dict) and key in current:
current = current[key]
else:
return None
return current
def update(self, values):
keys = self.path.split('/')
current = self.data
for key in keys[:-1]:
if key not in current:
current[key] = {}
current = current[key]
if keys[-1] not in current:
current[keys[-1]] = {}
current[keys[-1]].update(values)
return current[keys[-1]]
def _normalize_user_scope(user_id):
"""Normalize user scope value used in Firebase path building."""
if user_id is None:
return None
user_value = str(user_id).strip()
if not user_value or '/' in user_value or '\\' in user_value or '..' in user_value:
return None
return user_value
def reference(user_id=None):
"""Return a Firebase database reference scoped to the given user, or the root devices ref."""
if FIREBASE_AVAILABLE:
try:
user_scope = _normalize_user_scope(user_id)
if user_scope:
return db.reference(f'/users/{user_scope}/devices')
return db.reference('/devices')
except ValueError as e:
# Firebase is installed but not initialized (e.g. missing credentials in dev)
logger.warning("Firebase not initialized, falling back to mock data: %s", e)
return MockRef()
def _get_user_device_states_ref(user_id, device_id):
"""Centralized helper for building user-scoped device state Firebase references."""
user_scope = _normalize_user_scope(user_id)
if not user_scope or not device_id or '/' in str(device_id):
return None
return reference(user_scope).child(str(device_id)).child('states')