saviialib 1.4.0__tar.gz → 1.5.0__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.

Potentially problematic release.


This version of saviialib might be problematic. Click here for more details.

Files changed (89) hide show
  1. saviialib-1.5.0/PKG-INFO +126 -0
  2. saviialib-1.5.0/README.md +98 -0
  3. {saviialib-1.4.0 → saviialib-1.5.0}/pyproject.toml +3 -1
  4. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/__init__.py +2 -0
  5. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/general_types/api/saviia_api_types.py +1 -0
  6. saviialib-1.5.0/src/saviialib/general_types/api/saviia_netcamera_api_types.py +11 -0
  7. saviialib-1.5.0/src/saviialib/general_types/api/saviia_shakes_api_types.py +21 -0
  8. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/general_types/error_types/api/saviia_api_error_types.py +8 -0
  9. saviialib-1.5.0/src/saviialib/general_types/error_types/api/saviia_netcamera_error_types.py +7 -0
  10. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/general_types/error_types/common/common_types.py +9 -0
  11. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/directory_client/client/os_client.py +1 -1
  12. saviialib-1.5.0/src/saviialib/libs/ffmpeg_client/__init__.py +8 -0
  13. saviialib-1.5.0/src/saviialib/libs/ffmpeg_client/clients/ffmpeg_asyncio_client.py +101 -0
  14. saviialib-1.5.0/src/saviialib/libs/ffmpeg_client/ffmpeg_client.py +25 -0
  15. saviialib-1.5.0/src/saviialib/libs/ffmpeg_client/ffmpeg_client_contract.py +12 -0
  16. saviialib-1.5.0/src/saviialib/libs/ffmpeg_client/types/ffmpeg_client_types.py +28 -0
  17. saviialib-1.5.0/src/saviialib/libs/sftp_client/__init__.py +8 -0
  18. saviialib-1.5.0/src/saviialib/libs/sftp_client/clients/asyncssh_sftp_client.py +83 -0
  19. saviialib-1.5.0/src/saviialib/libs/sftp_client/sftp_client.py +26 -0
  20. saviialib-1.5.0/src/saviialib/libs/sftp_client/sftp_client_contract.py +13 -0
  21. saviialib-1.5.0/src/saviialib/libs/sftp_client/types/sftp_client_types.py +24 -0
  22. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/sharepoint_client/clients/sharepoint_rest_api.py +1 -0
  23. saviialib-1.5.0/src/saviialib/libs/zero_dependency/utils/strings_utils.py +5 -0
  24. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/backup/api.py +1 -1
  25. saviialib-1.5.0/src/saviialib/services/netcamera/api.py +30 -0
  26. saviialib-1.5.0/src/saviialib/services/netcamera/controllers/get_media_files.py +40 -0
  27. saviialib-1.5.0/src/saviialib/services/netcamera/controllers/types/get_media_files_types.py +16 -0
  28. saviialib-1.5.0/src/saviialib/services/netcamera/use_cases/get_media_files.py +76 -0
  29. saviialib-1.5.0/src/saviialib/services/netcamera/use_cases/types/get_media_files_types.py +18 -0
  30. saviialib-1.5.0/src/saviialib/services/shakes/api.py +31 -0
  31. saviialib-1.5.0/src/saviialib/services/shakes/controllers/get_miniseed_files.py +48 -0
  32. saviialib-1.5.0/src/saviialib/services/shakes/controllers/types/get_miniseed_files_types.py +16 -0
  33. saviialib-1.5.0/src/saviialib/services/shakes/use_cases/get_miniseed_files.py +79 -0
  34. saviialib-1.5.0/src/saviialib/services/shakes/use_cases/types/get_miniseed_files_types.py +18 -0
  35. saviialib-1.5.0/src/saviialib/services/shakes/use_cases/utils/get_miniseed_files_utils.py +11 -0
  36. saviialib-1.5.0/src/saviialib/services/thies/__init__.py +0 -0
  37. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/thies/use_cases/components/create_thies_statistics_file.py +2 -2
  38. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/thies/use_cases/types/update_thies_data_types.py +4 -1
  39. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/thies/use_cases/update_thies_data.py +4 -4
  40. saviialib-1.4.0/PKG-INFO +0 -124
  41. saviialib-1.4.0/README.md +0 -98
  42. {saviialib-1.4.0 → saviialib-1.5.0}/LICENSE +0 -0
  43. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/general_types/__init__.py +0 -0
  44. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/general_types/api/__init__.py +0 -0
  45. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/general_types/api/saviia_backup_api_types.py +0 -0
  46. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/general_types/api/saviia_thies_api_types.py +0 -0
  47. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/general_types/error_types/__init__.py +0 -0
  48. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/general_types/error_types/api/__init__.py +0 -0
  49. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/general_types/error_types/common/__init__.py +0 -0
  50. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/directory_client/__init__.py +0 -0
  51. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/directory_client/directory_client.py +0 -0
  52. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/directory_client/directory_client_contract.py +0 -0
  53. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/directory_client/types/directory_client_types.py +0 -0
  54. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/files_client/__init__.py +0 -0
  55. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/files_client/clients/aiofiles_client.py +0 -0
  56. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/files_client/clients/csv_client.py +0 -0
  57. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/files_client/files_client.py +0 -0
  58. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/files_client/files_client_contract.py +0 -0
  59. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/files_client/types/files_client_types.py +0 -0
  60. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/ftp_client/__init__.py +0 -0
  61. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/ftp_client/clients/__init__.py +0 -0
  62. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/ftp_client/clients/aioftp_client.py +0 -0
  63. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/ftp_client/clients/ftplib_client.py +0 -0
  64. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/ftp_client/ftp_client.py +0 -0
  65. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/ftp_client/ftp_client_contract.py +0 -0
  66. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/ftp_client/types/__init__.py +0 -0
  67. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/ftp_client/types/ftp_client_types.py +0 -0
  68. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/sharepoint_client/__init__.py +0 -0
  69. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/sharepoint_client/sharepoint_client.py +0 -0
  70. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/sharepoint_client/sharepoint_client_contract.py +0 -0
  71. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/sharepoint_client/types/sharepoint_client_types.py +0 -0
  72. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/libs/zero_dependency/utils/datetime_utils.py +0 -0
  73. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/backup/__init__.py +0 -0
  74. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/backup/controllers/__init__.py +0 -0
  75. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/backup/controllers/types/__init__.py +0 -0
  76. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/backup/controllers/types/upload_backup_to_sharepoint_types.py +0 -0
  77. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/backup/controllers/upload_backup_to_sharepoint.py +0 -0
  78. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/backup/use_cases/constants/upload_backup_to_sharepoint_constants.py +0 -0
  79. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/backup/use_cases/types/__init__.py +0 -0
  80. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/backup/use_cases/types/upload_backup_to_sharepoint_types.py +0 -0
  81. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/backup/use_cases/upload_backup_to_sharepoint.py +0 -0
  82. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/backup/utils/__init__.py +0 -0
  83. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/backup/utils/upload_backup_to_sharepoint_utils.py +0 -0
  84. {saviialib-1.4.0/src/saviialib/services/thies → saviialib-1.5.0/src/saviialib/services/shakes}/__init__.py +0 -0
  85. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/thies/api.py +0 -0
  86. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/thies/controllers/types/update_thies_data_types.py +0 -0
  87. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/thies/controllers/update_thies_data.py +0 -0
  88. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/thies/use_cases/components/thies_bp.py +0 -0
  89. {saviialib-1.4.0 → saviialib-1.5.0}/src/saviialib/services/thies/utils/update_thies_data_utils.py +0 -0
@@ -0,0 +1,126 @@
1
+ Metadata-Version: 2.4
2
+ Name: saviialib
3
+ Version: 1.5.0
4
+ Summary: A client library for IoT projects in the RCER initiative
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Author: pedropablozavalat
8
+ Requires-Python: >=3.11,<4.0
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Requires-Dist: aiofiles
16
+ Requires-Dist: aioftp
17
+ Requires-Dist: aiohttp
18
+ Requires-Dist: asyncssh (==2.21.1)
19
+ Requires-Dist: bitarray
20
+ Requires-Dist: build
21
+ Requires-Dist: certifi
22
+ Requires-Dist: dotenv (>=0.9.9,<0.10.0)
23
+ Requires-Dist: ffmpeg-asyncio (==0.1.3)
24
+ Requires-Dist: numpy (>=2.2.0,<2.4.0)
25
+ Requires-Dist: pandas (>=2.2.3,<2.3.1)
26
+ Description-Content-Type: text/markdown
27
+
28
+ # SAVIIA Library
29
+ *Sistema de Administración y Visualización de Información para la Investigación y Análisis*
30
+
31
+ [![GitHub release (latest by date)](https://img.shields.io/github/v/release/pedrozavalat/saviia-lib?style=for-the-badge)](https://github.com/pedrozavalat/saviia-lib/releases)
32
+
33
+
34
+ ## Installation
35
+ This library is designed for use with the SAVIIA Home Assistant Integration. It provides an API to retrieve files from a THIES Data Logger via an FTP server and upload them to a Microsoft SharePoint folder using the SharePoint REST API.
36
+
37
+ ```bash
38
+ pip install saviialib
39
+ ```
40
+
41
+ ## Saviia API Client Usage
42
+
43
+ ### Initialize the Saviia API Client
44
+ Import the necessary classes from the library.
45
+ ```python
46
+ from saviialib import SaviiaAPI, SaviiaAPIConfig
47
+ ```
48
+
49
+ To start using the library, you need to create an `SaviiaAPI` client instance with its configuration class `SaviiaAPIConfig`. Provide the required parameters such as FTP server details and SharePoint credentials:
50
+ ```python
51
+ config = SaviiaAPIConfig(
52
+ ftp_port=FTP_PORT,
53
+ ftp_host=FTP_HOST,
54
+ ftp_user=FTP_USER,
55
+ ftp_password=FTP_PASSWORD,
56
+ sharepoint_client_id=SHAREPOINT_CLIENT_ID,
57
+ sharepoint_client_secret=SHAREPOINT_CLIENT_SECRET,
58
+ sharepoint_tenant_id=SHAREPOINT_TENANT_ID,
59
+ sharepoint_tenant_name=SHAREPOINT_TENANT_NAME,
60
+ sharepoint_site_name=SHAREPOINT_SITE_NAME
61
+ )
62
+ ```
63
+ ```python
64
+ api_client = SaviiaAPI(config)
65
+ ```
66
+ **Notes:**
67
+ - Store sensitive data like `FTP_PASSWORD`, `FTP_USER`, and SharePoint credentials securely. Use environment variables or a secrets management tool to avoid hardcoding sensitive information in your codebase.
68
+
69
+ ### Access THIES Data Logger Services
70
+ To interact with the THIES Data Logger services, you can access the `thies` attribute of the `SaviiaAPI` instance:
71
+ ```python
72
+ thies_client = api_client.get('thies')
73
+ ```
74
+ This instance provides methods to interact with the THIES Data Logger. Currently, it includes the main method for extracting files from the FTP server and uploading them to SharePoint.
75
+
76
+ #### THIES files extraction and synchronization
77
+ The library provides a method to extract and synchronize THIES Data Logger files with the Microsoft SharePoint client. This method downloads files from the FTP server and uploads them to the specified SharePoint folder:
78
+ ```python
79
+ import asyncio
80
+ async def main():
81
+ # Before calling this method, you must have initialised the THIES service class ...
82
+ response = await thies_client.update_thies_data()
83
+ return response
84
+
85
+ asyncio.run(main())
86
+ ```
87
+
88
+ ### Access Backup Services
89
+ To interact with the Backup services, you can access the `backup` attribute of the `SaviiaAPI` instance:
90
+ ```python
91
+ backup_client = api_client.get('backup')
92
+ ```
93
+ This instance provides methods to interact with the Backup services. Currently, it includes the main method for creating backups of specified directories in a local folder from Home Assistant environment. Then each backup file is uploaded to a Microsoft SharePoint folder.
94
+
95
+ #### Create Backup
96
+ The library provides a method which creates a backup of a specified directory in a local folder from Home Assistant environment. Then each backup file is uploaded to a Microsoft SharePoint folder:
97
+
98
+ ```python
99
+ import asyncio
100
+ async def main():
101
+ # Before calling this method, you must have initialised the Backup service class ...
102
+ response = await backup_client.upload_backup_to_sharepoint(
103
+ local_backup_path=LOCAL_BACKUP_PATH,
104
+ sharepoint_folder_path=SHAREPOINT_FOLDER_PATH
105
+ )
106
+ return response
107
+ asyncio.run(main())
108
+ ```
109
+ **Notes:**
110
+ - Ensure that the `local_backup_path` exists and contains the files you want to back up. It is a relative path from the Home Assistant configuration directory.
111
+ - The `sharepoint_folder_path` should be the path to the folder in SharePoint where you want to upload the backup files. For example, if your url is `https://yourtenant.sharepoint.com/sites/yoursite/Shared Documents/Backups`, the folder path would be `sites/yoursite/Shared Documents/Backups`.
112
+
113
+
114
+
115
+
116
+
117
+
118
+
119
+ ## Contributing
120
+ If you're interested in contributing to this project, please follow the contributing guidelines. By contributing to this project, you agree to abide by its terms.
121
+ Contributions are welcome and appreciated!
122
+
123
+ ## License
124
+
125
+ `saviialib` was created by Pedro Pablo Zavala Tejos. It is licensed under the terms of the MIT license.
126
+
@@ -0,0 +1,98 @@
1
+ # SAVIIA Library
2
+ *Sistema de Administración y Visualización de Información para la Investigación y Análisis*
3
+
4
+ [![GitHub release (latest by date)](https://img.shields.io/github/v/release/pedrozavalat/saviia-lib?style=for-the-badge)](https://github.com/pedrozavalat/saviia-lib/releases)
5
+
6
+
7
+ ## Installation
8
+ This library is designed for use with the SAVIIA Home Assistant Integration. It provides an API to retrieve files from a THIES Data Logger via an FTP server and upload them to a Microsoft SharePoint folder using the SharePoint REST API.
9
+
10
+ ```bash
11
+ pip install saviialib
12
+ ```
13
+
14
+ ## Saviia API Client Usage
15
+
16
+ ### Initialize the Saviia API Client
17
+ Import the necessary classes from the library.
18
+ ```python
19
+ from saviialib import SaviiaAPI, SaviiaAPIConfig
20
+ ```
21
+
22
+ To start using the library, you need to create an `SaviiaAPI` client instance with its configuration class `SaviiaAPIConfig`. Provide the required parameters such as FTP server details and SharePoint credentials:
23
+ ```python
24
+ config = SaviiaAPIConfig(
25
+ ftp_port=FTP_PORT,
26
+ ftp_host=FTP_HOST,
27
+ ftp_user=FTP_USER,
28
+ ftp_password=FTP_PASSWORD,
29
+ sharepoint_client_id=SHAREPOINT_CLIENT_ID,
30
+ sharepoint_client_secret=SHAREPOINT_CLIENT_SECRET,
31
+ sharepoint_tenant_id=SHAREPOINT_TENANT_ID,
32
+ sharepoint_tenant_name=SHAREPOINT_TENANT_NAME,
33
+ sharepoint_site_name=SHAREPOINT_SITE_NAME
34
+ )
35
+ ```
36
+ ```python
37
+ api_client = SaviiaAPI(config)
38
+ ```
39
+ **Notes:**
40
+ - Store sensitive data like `FTP_PASSWORD`, `FTP_USER`, and SharePoint credentials securely. Use environment variables or a secrets management tool to avoid hardcoding sensitive information in your codebase.
41
+
42
+ ### Access THIES Data Logger Services
43
+ To interact with the THIES Data Logger services, you can access the `thies` attribute of the `SaviiaAPI` instance:
44
+ ```python
45
+ thies_client = api_client.get('thies')
46
+ ```
47
+ This instance provides methods to interact with the THIES Data Logger. Currently, it includes the main method for extracting files from the FTP server and uploading them to SharePoint.
48
+
49
+ #### THIES files extraction and synchronization
50
+ The library provides a method to extract and synchronize THIES Data Logger files with the Microsoft SharePoint client. This method downloads files from the FTP server and uploads them to the specified SharePoint folder:
51
+ ```python
52
+ import asyncio
53
+ async def main():
54
+ # Before calling this method, you must have initialised the THIES service class ...
55
+ response = await thies_client.update_thies_data()
56
+ return response
57
+
58
+ asyncio.run(main())
59
+ ```
60
+
61
+ ### Access Backup Services
62
+ To interact with the Backup services, you can access the `backup` attribute of the `SaviiaAPI` instance:
63
+ ```python
64
+ backup_client = api_client.get('backup')
65
+ ```
66
+ This instance provides methods to interact with the Backup services. Currently, it includes the main method for creating backups of specified directories in a local folder from Home Assistant environment. Then each backup file is uploaded to a Microsoft SharePoint folder.
67
+
68
+ #### Create Backup
69
+ The library provides a method which creates a backup of a specified directory in a local folder from Home Assistant environment. Then each backup file is uploaded to a Microsoft SharePoint folder:
70
+
71
+ ```python
72
+ import asyncio
73
+ async def main():
74
+ # Before calling this method, you must have initialised the Backup service class ...
75
+ response = await backup_client.upload_backup_to_sharepoint(
76
+ local_backup_path=LOCAL_BACKUP_PATH,
77
+ sharepoint_folder_path=SHAREPOINT_FOLDER_PATH
78
+ )
79
+ return response
80
+ asyncio.run(main())
81
+ ```
82
+ **Notes:**
83
+ - Ensure that the `local_backup_path` exists and contains the files you want to back up. It is a relative path from the Home Assistant configuration directory.
84
+ - The `sharepoint_folder_path` should be the path to the folder in SharePoint where you want to upload the backup files. For example, if your url is `https://yourtenant.sharepoint.com/sites/yoursite/Shared Documents/Backups`, the folder path would be `sites/yoursite/Shared Documents/Backups`.
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+ ## Contributing
93
+ If you're interested in contributing to this project, please follow the contributing guidelines. By contributing to this project, you agree to abide by its terms.
94
+ Contributions are welcome and appreciated!
95
+
96
+ ## License
97
+
98
+ `saviialib` was created by Pedro Pablo Zavala Tejos. It is licensed under the terms of the MIT license.
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "saviialib"
3
- version = "1.4.0"
3
+ version = "1.5.0"
4
4
  description = "A client library for IoT projects in the RCER initiative"
5
5
  authors = ["pedropablozavalat"]
6
6
  license = "MIT"
@@ -17,6 +17,8 @@ numpy = ">=2.2.0,<2.4.0"
17
17
  pandas = ">=2.2.3,<2.3.1"
18
18
  bitarray="*"
19
19
  certifi="*"
20
+ ffmpeg-asyncio="0.1.3"
21
+ asyncssh="2.21.1"
20
22
 
21
23
  [tool.poetry.group.dev.dependencies]
22
24
  pytest = "8.3.5"
@@ -12,6 +12,8 @@ from saviialib.services.thies.api import SaviiaThiesAPI
12
12
  from saviialib.general_types.api.saviia_thies_api_types import SaviiaThiesConfig
13
13
  from saviialib.general_types.api.saviia_backup_api_types import SaviiaBackupConfig
14
14
 
15
+ __all__ = ["SaviiaAPI", "SaviiaAPIConfig"]
16
+
15
17
 
16
18
  class SaviiaAPI:
17
19
  API_REGISTRY: Dict[str, Type] = {
@@ -30,6 +30,7 @@ class SaviiaAPIConfig:
30
30
  sharepoint_site_name: str
31
31
  logger: Logger
32
32
 
33
+
33
34
  @dataclass
34
35
  class FtpClientConfig:
35
36
  ftp_host: str
@@ -0,0 +1,11 @@
1
+ from dataclasses import dataclass
2
+ from logging import Logger
3
+
4
+
5
+ @dataclass
6
+ class SaviiaNetcameraConfig:
7
+ username: str
8
+ password: str
9
+ protocol: str
10
+ logger: Logger
11
+ destination_path: str = "/"
@@ -0,0 +1,21 @@
1
+ from dataclasses import dataclass
2
+ from logging import Logger
3
+
4
+
5
+ @dataclass
6
+ class SaviiaShakesConfig:
7
+ """
8
+ Configuration for Raspberry shakes activities as Miniseed extraction, photo record and video record.
9
+
10
+ Attributes:
11
+ sftp_user (str): Username for SFTP Client connection
12
+ sftp_password (str): Password for SFTP Client connection
13
+ sftp_port (str): SFTP Server Port. Default port is 22.
14
+ ssh_key_path (str): Path to the SSH Private key for client-side authentication.
15
+ """
16
+
17
+ sftp_user: str
18
+ sftp_password: str
19
+ ssh_key_path: str
20
+ logger: Logger
21
+ sftp_port: int = 22
@@ -103,3 +103,11 @@ class BackupEmptyError(Exception):
103
103
 
104
104
  def __str__(self):
105
105
  return "The local backup folder is empty. "
106
+
107
+
108
+ class ShakesNoContentError(Exception):
109
+ def __init__(self, *args):
110
+ super().__init__(*args)
111
+
112
+ def __str__(self):
113
+ return "All the miniSEED files have been downloaded and are in the local directory."
@@ -0,0 +1,7 @@
1
+ class NetcameraConnectionError(Exception):
2
+ def __init__(self, *args, reason):
3
+ super().__init__(*args, reason)
4
+ self.reason = reason
5
+
6
+ def __str__(self):
7
+ return "Netcamera Connection failed. " + self.reason.__str__()
@@ -15,3 +15,12 @@ class SharepointClientError(Exception):
15
15
  class FtpClientError(Exception):
16
16
  def __str__(self):
17
17
  return "Ftp Client initialization fails."
18
+
19
+
20
+ class SftpClientError(Exception):
21
+ def __init__(self, *args, reason):
22
+ super().__init__(*args, reason)
23
+ self.reason = reason
24
+
25
+ def __str__(self):
26
+ return "SFTP Client initialization fails." + self.reason.__str__()
@@ -19,7 +19,7 @@ class OsClient(DirectoryClientContract):
19
19
  return await asyncio.to_thread(os.listdir, path)
20
20
 
21
21
  @staticmethod
22
- async def isdir(path: str) -> list:
22
+ async def isdir(path: str) -> bool:
23
23
  return await asyncio.to_thread(os.path.isdir, path)
24
24
 
25
25
  @staticmethod
@@ -0,0 +1,8 @@
1
+ from .ffmpeg_client import (
2
+ FfmpegClient,
3
+ FfmpegClientInitArgs,
4
+ RecordPhotoArgs,
5
+ RecordVideoArgs,
6
+ )
7
+
8
+ __all__ = ["FfmpegClient", "FfmpegClientInitArgs", "RecordPhotoArgs", "RecordVideoArgs"]
@@ -0,0 +1,101 @@
1
+ from saviialib.libs.ffmpeg_client.types.ffmpeg_client_types import (
2
+ FfmpegClientInitArgs,
3
+ RecordPhotoArgs,
4
+ RecordVideoArgs,
5
+ )
6
+ from saviialib.libs.ffmpeg_client.ffmpeg_client_contract import FfmpegClientContract
7
+ from typing import List
8
+ import asyncio
9
+ import shutil
10
+
11
+ from saviialib.libs.directory_client import DirectoryClient, DirectoryClientArgs
12
+ from saviialib.libs.zero_dependency.utils.datetime_utils import today, datetime_to_str
13
+
14
+
15
+ class FfmpegAsyncioClient(FfmpegClientContract):
16
+ def __init__(self, args: FfmpegClientInitArgs) -> None:
17
+ self.dir_client = DirectoryClient(DirectoryClientArgs("os_client"))
18
+
19
+ def _setup_io_args(
20
+ self,
21
+ rtsp_user: str,
22
+ rtsp_pwd: str,
23
+ ip: str,
24
+ dest_path: str,
25
+ record_prefix: str,
26
+ record_type: str,
27
+ ):
28
+ input_arg = f"rtsp://{rtsp_user}:{rtsp_pwd}@{ip}/stream1"
29
+ output_file = (
30
+ record_prefix
31
+ + "_"
32
+ + datetime_to_str(today(), date_format="%m-%d-%Y_%H-%M-%S")
33
+ + f".{record_type}"
34
+ )
35
+ output_arg = self.dir_client.join_paths(dest_path, output_file)
36
+ return input_arg, output_arg
37
+
38
+ async def _ensure_ffmpeg_available(self):
39
+ if shutil.which("ffmpeg"):
40
+ return
41
+ install_cmd = ["apk", "add", "ffmpeg"] # Only for Home Assistant OS
42
+ process = await asyncio.create_subprocess_shell(
43
+ *install_cmd,
44
+ stdout=asyncio.subprocess.DEVNULL,
45
+ stdin=asyncio.subprocess.PIPE,
46
+ )
47
+ _, stderr = await process.communicate()
48
+ if process.returncode != 0:
49
+ raise ConnectionAbortedError("Failed to install ffmpeg: ", stderr.decode())
50
+
51
+ async def _setup_command(
52
+ self, input_arg: str, output_arg: str, extra: dict
53
+ ) -> List[str]:
54
+ await self._ensure_ffmpeg_available() # Validate ffmpeg module is installed.
55
+ cmd = ["ffmpeg", "-y", "-i", input_arg, output_arg]
56
+ for k, v in extra.values():
57
+ cmd.insert(-1, k)
58
+ cmd.insert(-1, v)
59
+ return list(map(str, cmd))
60
+
61
+ async def record_video(self, args: RecordVideoArgs):
62
+ input_arg, output_arg = self._setup_io_args(
63
+ args.rtsp_user,
64
+ args.rtsp_password,
65
+ args.ip_address,
66
+ args.destination_path,
67
+ "Video",
68
+ args.extension,
69
+ )
70
+ cmd = await self._setup_command(
71
+ input_arg, output_arg, extra={"-t": args.duration}
72
+ )
73
+ process = await asyncio.create_subprocess_exec(
74
+ *cmd, stderr=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE
75
+ )
76
+ _, stderr = await process.communicate()
77
+ if process.returncode != 0:
78
+ raise ConnectionError(
79
+ "Unexpected error while recording the video: ", stderr.decode()
80
+ )
81
+
82
+ async def record_photo(self, args: RecordPhotoArgs):
83
+ input_arg, output_arg = self._setup_io_args(
84
+ args.rtsp_user,
85
+ args.rtsp_password,
86
+ args.ip_address,
87
+ args.destination_path,
88
+ "Photo",
89
+ args.extension,
90
+ )
91
+ cmd = await self._setup_command(
92
+ input_arg, output_arg, extra={"-frames:v": args.frames}
93
+ )
94
+ process = await asyncio.create_subprocess_exec(
95
+ *cmd, stderr=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE
96
+ )
97
+ _, stderr = await process.communicate()
98
+ if process.returncode != 0:
99
+ raise ConnectionError(
100
+ "Unexpected error while recording the photo: ", stderr.decode()
101
+ )
@@ -0,0 +1,25 @@
1
+ from .ffmpeg_client_contract import FfmpegClientContract
2
+ from .types.ffmpeg_client_types import (
3
+ RecordPhotoArgs,
4
+ RecordVideoArgs,
5
+ FfmpegClientInitArgs,
6
+ )
7
+ from .clients.ffmpeg_asyncio_client import FfmpegAsyncioClient
8
+
9
+
10
+ class FfmpegClient(FfmpegClientContract):
11
+ CLIENTS = {"ffmpeg_asyncio"}
12
+
13
+ def __init__(self, args: FfmpegClientInitArgs) -> None:
14
+ if args.client_name not in FfmpegClient.CLIENTS:
15
+ msg = f"Unsupported client {args.client_name}"
16
+ raise KeyError(msg)
17
+ if args.client_name == "ffmpeg_asyncio":
18
+ self.client_obj = FfmpegAsyncioClient(args)
19
+ self.client_name = args.client_name
20
+
21
+ def record_photo(self, args: RecordPhotoArgs):
22
+ return self.client_obj.record_photo(args)
23
+
24
+ def record_video(self, args: RecordVideoArgs):
25
+ return self.client_obj.record_video(args)
@@ -0,0 +1,12 @@
1
+ from abc import ABC, abstractmethod
2
+ from .types.ffmpeg_client_types import RecordPhotoArgs, RecordVideoArgs
3
+
4
+
5
+ class FfmpegClientContract(ABC):
6
+ @abstractmethod
7
+ async def record_photo(self, args: RecordPhotoArgs):
8
+ pass
9
+
10
+ @abstractmethod
11
+ async def record_video(self, args: RecordVideoArgs):
12
+ pass
@@ -0,0 +1,28 @@
1
+ from dataclasses import dataclass
2
+
3
+
4
+ @dataclass
5
+ class FfmpegClientInitArgs:
6
+ client_name: str
7
+
8
+
9
+ @dataclass
10
+ class RecordPhotoArgs:
11
+ ip_address: str
12
+ destination_path: str
13
+ rtsp_user: str
14
+ rtsp_password: str
15
+ port: str
16
+ extension: str
17
+ frames: int
18
+
19
+
20
+ @dataclass
21
+ class RecordVideoArgs:
22
+ destination_path: str
23
+ ip_address: str
24
+ port: str
25
+ rtsp_user: str
26
+ rtsp_password: str
27
+ extension: str
28
+ duration: int
@@ -0,0 +1,8 @@
1
+ from .sftp_client import SFTPClient
2
+ from .types.sftp_client_types import (
3
+ ListfilesArgs,
4
+ DownloadfilesArgs,
5
+ SFTPClientInitArgs,
6
+ )
7
+
8
+ __all__ = ["SFTPClient", "ListfilesArgs", "DownloadfilesArgs", "SFTPClientInitArgs"]
@@ -0,0 +1,83 @@
1
+ from saviialib.libs.sftp_client.sftp_client_contract import SFTPClientContract
2
+ from saviialib.libs.sftp_client.types.sftp_client_types import (
3
+ SFTPClientInitArgs,
4
+ ListfilesArgs,
5
+ DownloadfilesArgs,
6
+ )
7
+ from typing import Optional, List, Tuple
8
+ from saviialib.general_types.error_types.common.common_types import EmptyDataError
9
+ import asyncssh # type: ignore
10
+ from saviialib.libs.directory_client import DirectoryClient, DirectoryClientArgs
11
+
12
+
13
+ class AsyncsshSFTPClient(SFTPClientContract):
14
+ def __init__(self, args: SFTPClientInitArgs) -> None:
15
+ self.host: str = args.host
16
+ self.port: int = args.port
17
+ self.username: str = args.username
18
+ self.password: Optional[str] = args.password
19
+ self.ssh_key_path: Optional[str] = args.ssh_key_path
20
+ self.dir_client = DirectoryClient(DirectoryClientArgs("os_client"))
21
+ self._validate_credentials()
22
+
23
+ def _validate_credentials(self):
24
+ if not self.password and not self.ssh_key_path:
25
+ raise EmptyDataError(
26
+ reason="At least one attribute (ssh key or password) must be provided"
27
+ )
28
+
29
+ async def _start_connection(
30
+ self,
31
+ ) -> Tuple[asyncssh.SSHClientConnection, asyncssh.SFTPClient]:
32
+ try:
33
+ ssh_connection = await asyncssh.connect(
34
+ self.host,
35
+ username=self.username,
36
+ port=self.port,
37
+ client_keys=[self.ssh_key_path],
38
+ password=self.password,
39
+ )
40
+ sftp_client = await ssh_connection.start_sftp_client()
41
+ return ssh_connection, sftp_client
42
+ except (OSError, asyncssh.Error) as exc:
43
+ raise ConnectionError("SFTP Operation failed: " + str(exc))
44
+
45
+ async def list_files(self, args: ListfilesArgs) -> List[str]:
46
+ ssh_conn, sftp_client = await self._start_connection()
47
+ async with ssh_conn:
48
+ async with sftp_client:
49
+ files = await sftp_client.listdir(args.path)
50
+ files = [f for f in files if f not in (".", "..")]
51
+ return files
52
+ await self._end_connection(ssh_conn)
53
+
54
+ async def download_files(self, args: DownloadfilesArgs) -> None:
55
+ ssh_conn, sftp_client = await self._start_connection()
56
+ if not args.destination_path or not args.source_path:
57
+ conflict_path = (
58
+ "destination path" if not args.destination_path else "source path"
59
+ )
60
+ raise ConnectionError(f"The {conflict_path} must be provided.")
61
+
62
+ download_all = len(args.files_to_download) == 0
63
+ async with ssh_conn:
64
+ async with sftp_client:
65
+ if download_all:
66
+ await sftp_client.get(
67
+ args.source_path,
68
+ args.destination_path,
69
+ recurse=True,
70
+ preserve=True,
71
+ )
72
+ else:
73
+ for filename in args.files_to_download:
74
+ source_path = self.dir_client.join_paths(
75
+ args.source_path, filename
76
+ )
77
+ dest_path = self.dir_client.join_paths(
78
+ args.destination_path, filename
79
+ )
80
+ await sftp_client.get(source_path, dest_path)
81
+
82
+ async def _end_connection(self, connection) -> None:
83
+ await connection.wait_closed()
@@ -0,0 +1,26 @@
1
+ from .sftp_client_contract import SFTPClientContract
2
+ from .types.sftp_client_types import (
3
+ SFTPClientInitArgs,
4
+ ListfilesArgs,
5
+ DownloadfilesArgs,
6
+ )
7
+ from .clients.asyncssh_sftp_client import AsyncsshSFTPClient
8
+ from typing import List
9
+
10
+
11
+ class SFTPClient(SFTPClientContract):
12
+ CLIENTS = {"asyncssh_sftp"}
13
+
14
+ def __init__(self, args: SFTPClientInitArgs) -> None:
15
+ if args.client_name not in SFTPClient.CLIENTS:
16
+ msg = f"Unsupported client {args.client_name}"
17
+ raise KeyError(msg)
18
+ if args.client_name == "asyncssh_sftp":
19
+ self.client_obj = AsyncsshSFTPClient(args)
20
+ self.client_name = args.client_name
21
+
22
+ async def list_files(self, args: ListfilesArgs) -> List[str]:
23
+ return await self.client_obj.list_files(args)
24
+
25
+ async def download_files(self, args: DownloadfilesArgs) -> None:
26
+ return await self.client_obj.download_files(args)
@@ -0,0 +1,13 @@
1
+ from .types.sftp_client_types import ListfilesArgs, DownloadfilesArgs
2
+ from abc import ABC, abstractmethod
3
+ from typing import List
4
+
5
+
6
+ class SFTPClientContract(ABC):
7
+ @abstractmethod
8
+ async def list_files(self, args: ListfilesArgs) -> List[str]:
9
+ pass
10
+
11
+ @abstractmethod
12
+ async def download_files(self, args: DownloadfilesArgs) -> None:
13
+ pass