iotlabkit 1.0.1__tar.gz → 1.0.3__tar.gz
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.
- {iotlabkit-1.0.1/src/iotlabkit.egg-info → iotlabkit-1.0.3}/PKG-INFO +1 -1
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/pyproject.toml +1 -1
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/__init__.py +1 -1
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/registry.py +3 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/templates/program_05.py +60 -19
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/templates/program_06.py +47 -16
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/templates/program_10.py +58 -17
- {iotlabkit-1.0.1 → iotlabkit-1.0.3/src/iotlabkit.egg-info}/PKG-INFO +1 -1
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/LICENSE +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/README.md +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/setup.cfg +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/cli.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/coap_util.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/data/beijing_air/beijing_sample.csv +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/data/india_air/city_day_sample.csv +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/data/occupancy/datatraining.txt +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/doctor.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/generator.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/templates/program_07.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/templates/program_08.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/templates/program_09.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/templates/program_11.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/templates/program_12.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/templates/program_13.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/templates/program_14.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/templates/program_15.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/templates/program_16.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit/templates/program_17.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit.egg-info/SOURCES.txt +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit.egg-info/dependency_links.txt +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit.egg-info/entry_points.txt +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit.egg-info/requires.txt +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/src/iotlabkit.egg-info/top_level.txt +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/tests/test_api_parsing.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/tests/test_cli.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/tests/test_coap_util.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/tests/test_doctor.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/tests/test_generator.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/tests/test_ml_pipeline.py +0 -0
- {iotlabkit-1.0.1 → iotlabkit-1.0.3}/tests/test_registry.py +0 -0
|
@@ -4,7 +4,7 @@ Designed for MSc/MCA Semester 3 IoT Laboratory practicals (Programs 5-17).
|
|
|
4
4
|
Generates clean, working, self-contained project templates in Learning and Exam modes.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
__version__ = "1.0.
|
|
7
|
+
__version__ = "1.0.3"
|
|
8
8
|
__author__ = "IoT Lab Contributors"
|
|
9
9
|
__license__ = "MIT"
|
|
10
10
|
|
|
@@ -81,6 +81,7 @@ PROGRAMS: Dict[int, ProgramInfo] = {
|
|
|
81
81
|
"dashboard.py",
|
|
82
82
|
"supabase_schema.sql",
|
|
83
83
|
"firebase_setup.md",
|
|
84
|
+
"init_db.py",
|
|
84
85
|
],
|
|
85
86
|
prerequisites=[
|
|
86
87
|
"Python 3.9+",
|
|
@@ -130,6 +131,7 @@ PROGRAMS: Dict[int, ProgramInfo] = {
|
|
|
130
131
|
"publisher.py",
|
|
131
132
|
"dashboard.py",
|
|
132
133
|
"supabase_schema.sql",
|
|
134
|
+
"init_db.py",
|
|
133
135
|
],
|
|
134
136
|
prerequisites=[
|
|
135
137
|
"Python 3.9+",
|
|
@@ -323,6 +325,7 @@ PROGRAMS: Dict[int, ProgramInfo] = {
|
|
|
323
325
|
"dashboard.py",
|
|
324
326
|
"supabase_schema.sql",
|
|
325
327
|
"coap_core.py",
|
|
328
|
+
"init_db.py",
|
|
326
329
|
],
|
|
327
330
|
prerequisites=[
|
|
328
331
|
"Python 3.9+",
|
|
@@ -16,10 +16,10 @@ def generate(target_dir: str, mode: str = "learning") -> Dict[str, str]:
|
|
|
16
16
|
files[".env.example"] = (
|
|
17
17
|
"PORT=5000\n"
|
|
18
18
|
"HOST=127.0.0.1\n"
|
|
19
|
-
"CLOUD_PROVIDER=
|
|
19
|
+
"CLOUD_PROVIDER=firebase\n"
|
|
20
|
+
"FIREBASE_DATABASE_URL=https://your-project.firebaseio.com\n"
|
|
20
21
|
"SUPABASE_URL=https://your-project.supabase.co\n"
|
|
21
22
|
"SUPABASE_KEY=your-supabase-key\n"
|
|
22
|
-
"FIREBASE_DATABASE_URL=https://your-project.firebaseio.com\n"
|
|
23
23
|
)
|
|
24
24
|
|
|
25
25
|
files[".gitignore"] = (
|
|
@@ -43,9 +43,10 @@ def generate(target_dir: str, mode: str = "learning") -> Dict[str, str]:
|
|
|
43
43
|
"# Firebase Setup Guide\n\n"
|
|
44
44
|
"1. Open https://console.firebase.google.com/ and create a project.\n"
|
|
45
45
|
"2. Go to Build -> Realtime Database -> Create Database.\n"
|
|
46
|
-
"3. Select test mode rules.\n"
|
|
47
|
-
"4. Copy database URL
|
|
48
|
-
"5.
|
|
46
|
+
"3. Select 'Start in test mode' rules.\n"
|
|
47
|
+
"4. Copy the database URL (e.g. https://your-project-rtdb.firebaseio.com/).\n"
|
|
48
|
+
"5. Paste the URL into .env under FIREBASE_DATABASE_URL.\n"
|
|
49
|
+
"6. Tables/paths are created automatically on first insert (zero SQL needed).\n"
|
|
49
50
|
)
|
|
50
51
|
|
|
51
52
|
# server.py
|
|
@@ -60,22 +61,28 @@ def generate(target_dir: str, mode: str = "learning") -> Dict[str, str]:
|
|
|
60
61
|
"app = Flask(__name__)\n\n"
|
|
61
62
|
"HOST = os.getenv('HOST', '127.0.0.1')\n"
|
|
62
63
|
"PORT = int(os.getenv('PORT', 5000))\n"
|
|
63
|
-
"CLOUD_PROVIDER = os.getenv('CLOUD_PROVIDER', '
|
|
64
|
+
"CLOUD_PROVIDER = os.getenv('CLOUD_PROVIDER', 'firebase').lower().strip()\n"
|
|
65
|
+
"FIREBASE_URL = os.getenv('FIREBASE_DATABASE_URL', '').rstrip('/')\n"
|
|
64
66
|
"SUPABASE_URL = os.getenv('SUPABASE_URL', '').rstrip('/')\n"
|
|
65
67
|
"SUPABASE_KEY = os.getenv('SUPABASE_KEY', '')\n"
|
|
66
|
-
"FIREBASE_URL = os.getenv('FIREBASE_DATABASE_URL', '').rstrip('/')\n"
|
|
67
68
|
"LOCAL_BACKUP = os.path.join(os.path.dirname(__file__), 'data', 'local_telemetry.json')\n\n"
|
|
69
|
+
"def save_to_firebase(record):\n"
|
|
70
|
+
" if not FIREBASE_URL or 'your-project' in FIREBASE_URL:\n"
|
|
71
|
+
" return False\n"
|
|
72
|
+
" try:\n"
|
|
73
|
+
" resp = requests.post(f'{FIREBASE_URL}/sensor_telemetry.json', json=record, timeout=5)\n"
|
|
74
|
+
" return resp.status_code == 200\n"
|
|
75
|
+
" except Exception:\n"
|
|
76
|
+
" return False\n\n"
|
|
68
77
|
"def save_to_supabase(record):\n"
|
|
69
78
|
" if not SUPABASE_URL or not SUPABASE_KEY or 'your-project' in SUPABASE_URL:\n"
|
|
70
79
|
" return False\n"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
" return False\n"
|
|
77
|
-
" resp = requests.post(f'{FIREBASE_URL}/sensor_telemetry.json', json=record, timeout=5)\n"
|
|
78
|
-
" return resp.status_code == 200\n\n"
|
|
80
|
+
" try:\n"
|
|
81
|
+
" headers = {'apikey': SUPABASE_KEY, 'Authorization': f'Bearer {SUPABASE_KEY}', 'Content-Type': 'application/json'}\n"
|
|
82
|
+
" resp = requests.post(f'{SUPABASE_URL}/rest/v1/sensor_telemetry', json=record, headers=headers, timeout=5)\n"
|
|
83
|
+
" return resp.status_code in (200, 201, 204)\n"
|
|
84
|
+
" except Exception:\n"
|
|
85
|
+
" return False\n\n"
|
|
79
86
|
"def save_local(record):\n"
|
|
80
87
|
" os.makedirs(os.path.dirname(LOCAL_BACKUP), exist_ok=True)\n"
|
|
81
88
|
" data = []\n"
|
|
@@ -100,16 +107,25 @@ def generate(target_dir: str, mode: str = "learning") -> Dict[str, str]:
|
|
|
100
107
|
" 'created_at': datetime.now(timezone.utc).isoformat()\n"
|
|
101
108
|
" }\n"
|
|
102
109
|
" saved_cloud = False\n"
|
|
103
|
-
" if CLOUD_PROVIDER == '
|
|
104
|
-
" saved_cloud = save_to_supabase(record)\n"
|
|
105
|
-
" elif CLOUD_PROVIDER == 'firebase':\n"
|
|
110
|
+
" if CLOUD_PROVIDER == 'firebase':\n"
|
|
106
111
|
" saved_cloud = save_to_firebase(record)\n"
|
|
112
|
+
" elif CLOUD_PROVIDER == 'supabase':\n"
|
|
113
|
+
" saved_cloud = save_to_supabase(record)\n"
|
|
107
114
|
" save_local(record)\n"
|
|
108
|
-
" status = 'Cloud
|
|
115
|
+
" status = 'Stored in Cloud' if saved_cloud else 'Saved Locally'\n"
|
|
109
116
|
" print(f\"[{datetime.now().strftime('%H:%M:%S')}] Telemetry: Temp={temp}C, Humidity={hum}% -> {status}\")\n"
|
|
110
117
|
" return jsonify({'message': 'Accepted', 'cloud_synced': saved_cloud, 'record': record}), 201\n\n"
|
|
111
118
|
"@app.route('/api/telemetry', methods=['GET'])\n"
|
|
112
119
|
"def get_telemetry():\n"
|
|
120
|
+
" if CLOUD_PROVIDER == 'firebase' and FIREBASE_URL and 'your-project' not in FIREBASE_URL:\n"
|
|
121
|
+
" try:\n"
|
|
122
|
+
" resp = requests.get(f'{FIREBASE_URL}/sensor_telemetry.json', timeout=4)\n"
|
|
123
|
+
" if resp.status_code == 200 and resp.json():\n"
|
|
124
|
+
" data = resp.json()\n"
|
|
125
|
+
" if isinstance(data, dict):\n"
|
|
126
|
+
" return jsonify(list(reversed(list(data.values())))), 200\n"
|
|
127
|
+
" except Exception:\n"
|
|
128
|
+
" pass\n"
|
|
113
129
|
" if os.path.exists(LOCAL_BACKUP):\n"
|
|
114
130
|
" with open(LOCAL_BACKUP, 'r', encoding='utf-8') as f:\n"
|
|
115
131
|
" data = json.load(f)\n"
|
|
@@ -120,6 +136,31 @@ def generate(target_dir: str, mode: str = "learning") -> Dict[str, str]:
|
|
|
120
136
|
" app.run(host=HOST, port=PORT, debug=False)\n"
|
|
121
137
|
)
|
|
122
138
|
|
|
139
|
+
files["init_db.py"] = (
|
|
140
|
+
"import os\n"
|
|
141
|
+
"import requests\n"
|
|
142
|
+
"from dotenv import load_dotenv\n\n"
|
|
143
|
+
"load_dotenv()\n\n"
|
|
144
|
+
"FIREBASE_URL = os.getenv('FIREBASE_DATABASE_URL', '').rstrip('/')\n"
|
|
145
|
+
"SUPABASE_URL = os.getenv('SUPABASE_URL', '').rstrip('/')\n\n"
|
|
146
|
+
"def check():\n"
|
|
147
|
+
" print('Database Check:')\n"
|
|
148
|
+
" if FIREBASE_URL and 'your-project' not in FIREBASE_URL:\n"
|
|
149
|
+
" try:\n"
|
|
150
|
+
" r = requests.get(f'{FIREBASE_URL}/.json?shallow=true', timeout=5)\n"
|
|
151
|
+
" if r.status_code == 200:\n"
|
|
152
|
+
" print('Firebase: Connected. Tables are created automatically on first insert.')\n"
|
|
153
|
+
" return True\n"
|
|
154
|
+
" except Exception as e:\n"
|
|
155
|
+
" print('Firebase note:', e)\n"
|
|
156
|
+
" if SUPABASE_URL and 'your-project' not in SUPABASE_URL:\n"
|
|
157
|
+
" print('Supabase configured. Schema is in supabase_schema.sql.')\n"
|
|
158
|
+
" print('Local database: Ready (data/local_telemetry.json)')\n"
|
|
159
|
+
" return True\n\n"
|
|
160
|
+
"if __name__ == '__main__':\n"
|
|
161
|
+
" check()\n"
|
|
162
|
+
)
|
|
163
|
+
|
|
123
164
|
# client_sender.py
|
|
124
165
|
files["client_sender.py"] = (
|
|
125
166
|
"import argparse\n"
|
|
@@ -17,10 +17,10 @@ def generate(target_dir: str, mode: str = "learning") -> Dict[str, str]:
|
|
|
17
17
|
"MQTT_BROKER=broker.hivemq.com\n"
|
|
18
18
|
"MQTT_PORT=1883\n"
|
|
19
19
|
"MQTT_TOPIC=iot/lab/telemetry\n"
|
|
20
|
-
"CLOUD_PROVIDER=
|
|
20
|
+
"CLOUD_PROVIDER=firebase\n"
|
|
21
|
+
"FIREBASE_DATABASE_URL=https://your-project.firebaseio.com\n"
|
|
21
22
|
"SUPABASE_URL=https://your-project.supabase.co\n"
|
|
22
23
|
"SUPABASE_KEY=your-supabase-key\n"
|
|
23
|
-
"FIREBASE_DATABASE_URL=https://your-project.firebaseio.com\n"
|
|
24
24
|
)
|
|
25
25
|
|
|
26
26
|
files[".gitignore"] = (
|
|
@@ -55,22 +55,28 @@ def generate(target_dir: str, mode: str = "learning") -> Dict[str, str]:
|
|
|
55
55
|
"BROKER = os.getenv('MQTT_BROKER', 'broker.hivemq.com')\n"
|
|
56
56
|
"PORT = int(os.getenv('MQTT_PORT', 1883))\n"
|
|
57
57
|
"TOPIC = os.getenv('MQTT_TOPIC', 'iot/lab/telemetry')\n"
|
|
58
|
-
"CLOUD_PROVIDER = os.getenv('CLOUD_PROVIDER', '
|
|
58
|
+
"CLOUD_PROVIDER = os.getenv('CLOUD_PROVIDER', 'firebase').lower().strip()\n"
|
|
59
|
+
"FIREBASE_URL = os.getenv('FIREBASE_DATABASE_URL', '').rstrip('/')\n"
|
|
59
60
|
"SUPABASE_URL = os.getenv('SUPABASE_URL', '').rstrip('/')\n"
|
|
60
61
|
"SUPABASE_KEY = os.getenv('SUPABASE_KEY', '')\n"
|
|
61
|
-
"FIREBASE_URL = os.getenv('FIREBASE_DATABASE_URL', '').rstrip('/')\n"
|
|
62
62
|
"LOCAL_BACKUP = os.path.join(os.path.dirname(__file__), 'data', 'local_mqtt_telemetry.json')\n\n"
|
|
63
|
-
"def save_to_supabase(record):\n"
|
|
64
|
-
" if not SUPABASE_URL or not SUPABASE_KEY or 'your-project' in SUPABASE_URL:\n"
|
|
65
|
-
" return False\n"
|
|
66
|
-
" headers = {'apikey': SUPABASE_KEY, 'Authorization': f'Bearer {SUPABASE_KEY}', 'Content-Type': 'application/json'}\n"
|
|
67
|
-
" resp = requests.post(f'{SUPABASE_URL}/rest/v1/mqtt_telemetry', json=record, headers=headers, timeout=5)\n"
|
|
68
|
-
" return resp.status_code in (200, 201, 204)\n\n"
|
|
69
63
|
"def save_to_firebase(record):\n"
|
|
70
64
|
" if not FIREBASE_URL or 'your-firebase' in FIREBASE_URL:\n"
|
|
71
65
|
" return False\n"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
66
|
+
" try:\n"
|
|
67
|
+
" resp = requests.post(f'{FIREBASE_URL}/mqtt_telemetry.json', json=record, timeout=5)\n"
|
|
68
|
+
" return resp.status_code == 200\n"
|
|
69
|
+
" except Exception:\n"
|
|
70
|
+
" return False\n\n"
|
|
71
|
+
"def save_to_supabase(record):\n"
|
|
72
|
+
" if not SUPABASE_URL or not SUPABASE_KEY or 'your-project' in SUPABASE_URL:\n"
|
|
73
|
+
" return False\n"
|
|
74
|
+
" try:\n"
|
|
75
|
+
" headers = {'apikey': SUPABASE_KEY, 'Authorization': f'Bearer {SUPABASE_KEY}', 'Content-Type': 'application/json'}\n"
|
|
76
|
+
" resp = requests.post(f'{SUPABASE_URL}/rest/v1/mqtt_telemetry', json=record, headers=headers, timeout=5)\n"
|
|
77
|
+
" return resp.status_code in (200, 201, 204)\n"
|
|
78
|
+
" except Exception:\n"
|
|
79
|
+
" return False\n\n"
|
|
74
80
|
"def save_local(record):\n"
|
|
75
81
|
" os.makedirs(os.path.dirname(LOCAL_BACKUP), exist_ok=True)\n"
|
|
76
82
|
" data = []\n"
|
|
@@ -103,12 +109,12 @@ def generate(target_dir: str, mode: str = "learning") -> Dict[str, str]:
|
|
|
103
109
|
" 'created_at': datetime.now(timezone.utc).isoformat()\n"
|
|
104
110
|
" }\n"
|
|
105
111
|
" saved = False\n"
|
|
106
|
-
" if CLOUD_PROVIDER == '
|
|
107
|
-
" saved = save_to_supabase(record)\n"
|
|
108
|
-
" elif CLOUD_PROVIDER == 'firebase':\n"
|
|
112
|
+
" if CLOUD_PROVIDER == 'firebase':\n"
|
|
109
113
|
" saved = save_to_firebase(record)\n"
|
|
114
|
+
" elif CLOUD_PROVIDER == 'supabase':\n"
|
|
115
|
+
" saved = save_to_supabase(record)\n"
|
|
110
116
|
" save_local(record)\n"
|
|
111
|
-
" status = 'Cloud
|
|
117
|
+
" status = 'Stored in Cloud' if saved else 'Saved Locally'\n"
|
|
112
118
|
" print(f\"[{datetime.now().strftime('%H:%M:%S')}] Received on {msg.topic}: Temp={temp}C, Hum={hum}% -> {status}\")\n"
|
|
113
119
|
" except Exception as e:\n"
|
|
114
120
|
" print('Message parse error:', e)\n\n"
|
|
@@ -123,6 +129,31 @@ def generate(target_dir: str, mode: str = "learning") -> Dict[str, str]:
|
|
|
123
129
|
" main()\n"
|
|
124
130
|
)
|
|
125
131
|
|
|
132
|
+
files["init_db.py"] = (
|
|
133
|
+
"import os\n"
|
|
134
|
+
"import requests\n"
|
|
135
|
+
"from dotenv import load_dotenv\n\n"
|
|
136
|
+
"load_dotenv()\n\n"
|
|
137
|
+
"FIREBASE_URL = os.getenv('FIREBASE_DATABASE_URL', '').rstrip('/')\n"
|
|
138
|
+
"SUPABASE_URL = os.getenv('SUPABASE_URL', '').rstrip('/')\n\n"
|
|
139
|
+
"def check():\n"
|
|
140
|
+
" print('Database Check:')\n"
|
|
141
|
+
" if FIREBASE_URL and 'your-project' not in FIREBASE_URL:\n"
|
|
142
|
+
" try:\n"
|
|
143
|
+
" r = requests.get(f'{FIREBASE_URL}/.json?shallow=true', timeout=5)\n"
|
|
144
|
+
" if r.status_code == 200:\n"
|
|
145
|
+
" print('Firebase: Connected. Tables are created automatically on first insert.')\n"
|
|
146
|
+
" return True\n"
|
|
147
|
+
" except Exception as e:\n"
|
|
148
|
+
" print('Firebase note:', e)\n"
|
|
149
|
+
" if SUPABASE_URL and 'your-project' not in SUPABASE_URL:\n"
|
|
150
|
+
" print('Supabase configured. Schema is in supabase_schema.sql.')\n"
|
|
151
|
+
" print('Local database: Ready (data/local_mqtt_telemetry.json)')\n"
|
|
152
|
+
" return True\n\n"
|
|
153
|
+
"if __name__ == '__main__':\n"
|
|
154
|
+
" check()\n"
|
|
155
|
+
)
|
|
156
|
+
|
|
126
157
|
# publisher.py
|
|
127
158
|
files["publisher.py"] = (
|
|
128
159
|
"import argparse\n"
|
|
@@ -18,10 +18,10 @@ def generate(target_dir: str, mode: str = "learning") -> Dict[str, str]:
|
|
|
18
18
|
"LONGITUDE=76.3419\n"
|
|
19
19
|
"COAP_HOST=127.0.0.1\n"
|
|
20
20
|
"COAP_PORT=56830\n"
|
|
21
|
-
"CLOUD_PROVIDER=
|
|
21
|
+
"CLOUD_PROVIDER=firebase\n"
|
|
22
|
+
"FIREBASE_DATABASE_URL=https://your-firebase-project.firebaseio.com\n"
|
|
22
23
|
"SUPABASE_URL=https://your-project-ref.supabase.co\n"
|
|
23
24
|
"SUPABASE_KEY=your-supabase-anon-key\n"
|
|
24
|
-
"FIREBASE_DATABASE_URL=https://your-firebase-project.firebaseio.com\n"
|
|
25
25
|
)
|
|
26
26
|
|
|
27
27
|
files[".gitignore"] = (
|
|
@@ -143,22 +143,28 @@ def generate(target_dir: str, mode: str = "learning") -> Dict[str, str]:
|
|
|
143
143
|
"load_dotenv()\n\n"
|
|
144
144
|
"COAP_HOST = os.getenv('COAP_HOST', '127.0.0.1')\n"
|
|
145
145
|
"COAP_PORT = int(os.getenv('COAP_PORT', 56830))\n"
|
|
146
|
-
"CLOUD_PROVIDER = os.getenv('CLOUD_PROVIDER', '
|
|
146
|
+
"CLOUD_PROVIDER = os.getenv('CLOUD_PROVIDER', 'firebase').lower().strip()\n"
|
|
147
|
+
"FIREBASE_URL = os.getenv('FIREBASE_DATABASE_URL', '').rstrip('/')\n"
|
|
147
148
|
"SUPABASE_URL = os.getenv('SUPABASE_URL', '').rstrip('/')\n"
|
|
148
149
|
"SUPABASE_KEY = os.getenv('SUPABASE_KEY', '')\n"
|
|
149
|
-
"FIREBASE_URL = os.getenv('FIREBASE_DATABASE_URL', '').rstrip('/')\n"
|
|
150
150
|
"LOCAL_BACKUP = os.path.join(os.path.dirname(__file__), 'data', 'local_coap_env.json')\n\n"
|
|
151
|
-
"def save_to_supabase(record):\n"
|
|
152
|
-
" if not SUPABASE_URL or not SUPABASE_KEY or 'your-project' in SUPABASE_URL:\n"
|
|
153
|
-
" return False\n"
|
|
154
|
-
" headers = {'apikey': SUPABASE_KEY, 'Authorization': f'Bearer {SUPABASE_KEY}', 'Content-Type': 'application/json'}\n"
|
|
155
|
-
" resp = requests.post(f'{SUPABASE_URL}/rest/v1/coap_environmental_records', json=record, headers=headers, timeout=5)\n"
|
|
156
|
-
" return resp.status_code in (200, 201, 204)\n\n"
|
|
157
151
|
"def save_to_firebase(record):\n"
|
|
158
152
|
" if not FIREBASE_URL or 'your-firebase' in FIREBASE_URL:\n"
|
|
159
153
|
" return False\n"
|
|
160
|
-
"
|
|
161
|
-
"
|
|
154
|
+
" try:\n"
|
|
155
|
+
" resp = requests.post(f'{FIREBASE_URL}/coap_environmental_records.json', json=record, timeout=5)\n"
|
|
156
|
+
" return resp.status_code == 200\n"
|
|
157
|
+
" except Exception:\n"
|
|
158
|
+
" return False\n\n"
|
|
159
|
+
"def save_to_supabase(record):\n"
|
|
160
|
+
" if not SUPABASE_URL or not SUPABASE_KEY or 'your-project' in SUPABASE_URL:\n"
|
|
161
|
+
" return False\n"
|
|
162
|
+
" try:\n"
|
|
163
|
+
" headers = {'apikey': SUPABASE_KEY, 'Authorization': f'Bearer {SUPABASE_KEY}', 'Content-Type': 'application/json'}\n"
|
|
164
|
+
" resp = requests.post(f'{SUPABASE_URL}/rest/v1/coap_environmental_records', json=record, headers=headers, timeout=5)\n"
|
|
165
|
+
" return resp.status_code in (200, 201, 204)\n"
|
|
166
|
+
" except Exception:\n"
|
|
167
|
+
" return False\n\n"
|
|
162
168
|
"def save_locally(record):\n"
|
|
163
169
|
" os.makedirs(os.path.dirname(LOCAL_BACKUP), exist_ok=True)\n"
|
|
164
170
|
" records = []\n"
|
|
@@ -193,12 +199,12 @@ def generate(target_dir: str, mode: str = "learning") -> Dict[str, str]:
|
|
|
193
199
|
" print(f\" O3 : {data.get('ozone')} ug/m3\")\n"
|
|
194
200
|
" print(f\" European AQI : {data.get('european_aqi')}\")\n\n"
|
|
195
201
|
" cloud_ok = False\n"
|
|
196
|
-
" if CLOUD_PROVIDER == '
|
|
197
|
-
" cloud_ok = save_to_supabase(data)\n"
|
|
198
|
-
" elif CLOUD_PROVIDER == 'firebase':\n"
|
|
202
|
+
" if CLOUD_PROVIDER == 'firebase':\n"
|
|
199
203
|
" cloud_ok = save_to_firebase(data)\n"
|
|
204
|
+
" elif CLOUD_PROVIDER == 'supabase':\n"
|
|
205
|
+
" cloud_ok = save_to_supabase(data)\n"
|
|
200
206
|
" save_locally(data)\n\n"
|
|
201
|
-
" status_msg = 'Stored in
|
|
207
|
+
" status_msg = 'Stored in Cloud' if cloud_ok else 'Saved locally'\n"
|
|
202
208
|
" print(f\"Status: {status_msg}\")\n"
|
|
203
209
|
" except Exception as err:\n"
|
|
204
210
|
" print(f\"Request failed: {err}\")\n\n"
|
|
@@ -206,6 +212,31 @@ def generate(target_dir: str, mode: str = "learning") -> Dict[str, str]:
|
|
|
206
212
|
" main()\n"
|
|
207
213
|
)
|
|
208
214
|
|
|
215
|
+
files["init_db.py"] = (
|
|
216
|
+
"import os\n"
|
|
217
|
+
"import requests\n"
|
|
218
|
+
"from dotenv import load_dotenv\n\n"
|
|
219
|
+
"load_dotenv()\n\n"
|
|
220
|
+
"FIREBASE_URL = os.getenv('FIREBASE_DATABASE_URL', '').rstrip('/')\n"
|
|
221
|
+
"SUPABASE_URL = os.getenv('SUPABASE_URL', '').rstrip('/')\n\n"
|
|
222
|
+
"def check():\n"
|
|
223
|
+
" print('Database Check:')\n"
|
|
224
|
+
" if FIREBASE_URL and 'your-firebase' not in FIREBASE_URL:\n"
|
|
225
|
+
" try:\n"
|
|
226
|
+
" r = requests.get(f'{FIREBASE_URL}/.json?shallow=true', timeout=5)\n"
|
|
227
|
+
" if r.status_code == 200:\n"
|
|
228
|
+
" print('Firebase: Connected. Tables are created automatically on first insert.')\n"
|
|
229
|
+
" return True\n"
|
|
230
|
+
" except Exception as e:\n"
|
|
231
|
+
" print('Firebase note:', e)\n"
|
|
232
|
+
" if SUPABASE_URL and 'your-project' not in SUPABASE_URL:\n"
|
|
233
|
+
" print('Supabase configured. Schema is in supabase_schema.sql.')\n"
|
|
234
|
+
" print('Local database: Ready (data/local_coap_env.json)')\n"
|
|
235
|
+
" return True\n\n"
|
|
236
|
+
"if __name__ == '__main__':\n"
|
|
237
|
+
" check()\n"
|
|
238
|
+
)
|
|
239
|
+
|
|
209
240
|
files["dashboard.py"] = (
|
|
210
241
|
"import os\n"
|
|
211
242
|
"import json\n"
|
|
@@ -214,11 +245,21 @@ def generate(target_dir: str, mode: str = "learning") -> Dict[str, str]:
|
|
|
214
245
|
"from dotenv import load_dotenv\n\n"
|
|
215
246
|
"load_dotenv()\n\n"
|
|
216
247
|
"st.title('CoAP Environmental Monitoring')\n\n"
|
|
217
|
-
"CLOUD_PROVIDER = os.getenv('CLOUD_PROVIDER', '
|
|
248
|
+
"CLOUD_PROVIDER = os.getenv('CLOUD_PROVIDER', 'firebase').lower().strip()\n"
|
|
249
|
+
"FIREBASE_URL = os.getenv('FIREBASE_DATABASE_URL', '').rstrip('/')\n"
|
|
218
250
|
"SUPABASE_URL = os.getenv('SUPABASE_URL', '').rstrip('/')\n"
|
|
219
251
|
"SUPABASE_KEY = os.getenv('SUPABASE_KEY', '')\n"
|
|
220
252
|
"LOCAL_BACKUP = os.path.join(os.path.dirname(__file__), 'data', 'local_coap_env.json')\n\n"
|
|
221
253
|
"def fetch_records():\n"
|
|
254
|
+
" if CLOUD_PROVIDER == 'firebase' and FIREBASE_URL and 'your-firebase' not in FIREBASE_URL:\n"
|
|
255
|
+
" try:\n"
|
|
256
|
+
" resp = requests.get(f'{FIREBASE_URL}/coap_environmental_records.json', timeout=4)\n"
|
|
257
|
+
" if resp.status_code == 200 and resp.json():\n"
|
|
258
|
+
" data = resp.json()\n"
|
|
259
|
+
" if isinstance(data, dict):\n"
|
|
260
|
+
" return list(reversed(list(data.values())))\n"
|
|
261
|
+
" except Exception:\n"
|
|
262
|
+
" pass\n"
|
|
222
263
|
" if CLOUD_PROVIDER == 'supabase' and SUPABASE_URL and 'your-project' not in SUPABASE_URL:\n"
|
|
223
264
|
" try:\n"
|
|
224
265
|
" headers = {'apikey': SUPABASE_KEY, 'Authorization': f'Bearer {SUPABASE_KEY}'}\n"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|