sensor_new.html 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. {% extends "base.html" %} {% set active_page = "assets" %} {% block title %} New
  2. sensor {% endblock %} {% block divs %}
  3. <div class="container-fluid">
  4. <div class="row">
  5. <div class="col-md-12">
  6. <button class="btn btn-sm btn-responsive btn-info" type="submit">
  7. <a href="/assets/{{asset.id}}">Go to parent asset</a>
  8. </button>
  9. </div>
  10. </div>
  11. <form id="sensorForm" class="form p-4" method="POST">
  12. <div
  13. id="alertbox"
  14. class="alert alert-success"
  15. role="alert"
  16. style="display: none"
  17. ></div>
  18. <div class="mb-3">
  19. <label for="name" class="form-label">Name</label>
  20. <input
  21. type="text"
  22. class="form-control"
  23. id="name"
  24. name="name"
  25. placeholder="e.g power"
  26. required
  27. />
  28. </div>
  29. <div class="mb-3">
  30. <label for="eventResolution" class="form-label"
  31. >Event Resolution
  32. <i
  33. class="fa fa-info-circle ps-2"
  34. data-bs-toggle="tooltip"
  35. data-bs-placement="bottom"
  36. title="Enter an ISO 8601 duration (e.g., PT1H for one hour, PT15M for 15 minutes)."
  37. style="font-size: 16px"
  38. ></i>
  39. </label>
  40. <input
  41. type="text"
  42. class="form-control"
  43. id="eventResolution"
  44. name="event_resolution"
  45. placeholder="Expected resolution of the sensor's data. E.g. PT1H (which is one hour) or PT15M (15 minutes)"
  46. required
  47. />
  48. </div>
  49. <div class="mb-3">
  50. <label for="unit" class="form-label"
  51. >Unit
  52. <i
  53. class="fa fa-info-circle ps-2"
  54. data-bs-toggle="tooltip"
  55. data-bs-placement="bottom"
  56. title="The unit of the sensor's data (e.g. kW or EUR/MWh). Choose a unit from the list or type a custom unit (ensure it's recognized by 'pint' library)."
  57. style="font-size: 16px"
  58. ></i>
  59. </label>
  60. <div class="row">
  61. <div class="col-4 pe-0">
  62. <select
  63. class="form-select"
  64. onchange="document.getElementById('unit').value = this.value"
  65. >
  66. <option selected>Choose from units already in use</option>
  67. {% for unit in available_units %}
  68. <option>{{ unit }}</option>
  69. {% endfor %}
  70. </select>
  71. </div>
  72. <div class="col-8 ps-1">
  73. <input
  74. type="text"
  75. class="form-control"
  76. id="unit"
  77. name="unit"
  78. placeholder="Type in any unit by hand, e.g. 'kW' or 'EUR/kWh'"
  79. required
  80. />
  81. </div>
  82. </div>
  83. </div>
  84. <button type="submit" class="btn btn-primary" onclick="createSensor(event)">
  85. Create
  86. </button>
  87. </form>
  88. </div>
  89. <script type="text/javascript">
  90. const apiBasePath = window.location.origin;
  91. const sensorForm = document.getElementById("sensorForm");
  92. const alertBox = document.getElementById("alertbox");
  93. function validateEventResolution(eventResolution) {
  94. // Regular expression to match duration format (PT[H|M|S])
  95. const regex = /^PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?$/;
  96. if (!regex.test(eventResolution)) {
  97. return false;
  98. }
  99. const matches = regex.exec(eventResolution);
  100. // At least one of H, M, or S must be present.
  101. if (!matches[1] && !matches[2] && !matches[3]) {
  102. return false;
  103. }
  104. let hours = parseInt(matches[1]) || 0;
  105. let minutes = parseInt(matches[2]) || 0;
  106. let seconds = parseInt(matches[3]) || 0;
  107. if (hours > 24) {
  108. return false;
  109. }
  110. return true;
  111. }
  112. async function createSensor(event) {
  113. event.preventDefault();
  114. const name = document.getElementById("name").value;
  115. const eventResolution = document.getElementById("eventResolution").value;
  116. const unit = document.getElementById("unit").value;
  117. if (!name || !eventResolution || !unit) {
  118. showToast("Please fill in all fields.", "error");
  119. return;
  120. }
  121. if (!validateEventResolution(eventResolution)) {
  122. showToast(
  123. `Invalid event resolution format "${eventResolution}". Please use ISO 8601 duration format (PT[H|M|S]).`,
  124. "error"
  125. );
  126. return;
  127. }
  128. const data = {
  129. name: name,
  130. event_resolution: eventResolution,
  131. unit: unit,
  132. generic_asset_id: "{{ asset.id }}",
  133. };
  134. const response = await fetch(apiBasePath + "/api/v3_0/sensors", {
  135. method: "POST",
  136. headers: {
  137. "Content-Type": "application/json",
  138. },
  139. body: JSON.stringify(data),
  140. });
  141. if (response.ok) {
  142. const responseData = await response.json();
  143. const sensorId = responseData.id;
  144. showToast("Sensor created successfully.", "success");
  145. alertBox.innerHTML = `Sensor created successfully. You can visit your new sensor <a target="_blank" href='/sensors/${sensorId}'>here</a>.`;
  146. alertBox.style.display = "block";
  147. // set all fields value to empty string
  148. document.getElementById("name").value = "";
  149. document.getElementById("eventResolution").value = "";
  150. document.getElementById("unit").value = "";
  151. } else {
  152. const errorData = await response.json();
  153. let errorMessage = response.statusText; // Default to statusText
  154. const messageUnitError = errorData?.message?.json?.unit?.[0];
  155. if (messageUnitError) {
  156. errorMessage = messageUnitError;
  157. } else if (errorData?.message) {
  158. errorMessage = errorData.message;
  159. } else if (errorData?.error) {
  160. errorMessage = errorData.error;
  161. }
  162. showToast("Error: " + errorMessage, "error");
  163. }
  164. }
  165. </script>
  166. {% endblock %}