Popular New Releases in Wifi
esp8266_deauther
Version 2.6.1
itlwm
v2.1.0 stable
wifi-password
Release 1.1.1
TinyCheck
New version of TinyCheck with MISP implementation!
VPNHotspot
v2.13.5
Popular Libraries in Wifi
by SpacehuhnTech c
8891 NOASSERTION
Affordable WiFi hacking platform for testing and learning
by schollz python
6501 MIT
Count the number of people around you :family_man_man_boy: by monitoring wifi signals :satellite:
by OpenIntelWireless c
5394 NOASSERTION
Intel Wi-Fi Drivers for macOS
by kootenpv python
4569 AGPL-3.0
Uses WiFi signals :signal_strength: and machine learning to predict where you are
by oblique shell
4105 BSD-2-Clause
[NOT MAINTAINED] This script creates a NATed or Bridged WiFi Access Point.
by DanMcInerney python
3258
Continuously jam all wifi clients/routers
by derv82 python
3244 GPL-2.0
Rewrite of the popular wireless network auditor, "wifite"
by kylemcdonald python
2684 NOASSERTION
How to get free wifi.
by feross python
2624
:briefcase: Change your MAC address for debugging
Trending New libraries in Wifi
by OpenIntelWireless c
5394 NOASSERTION
Intel Wi-Fi Drivers for macOS
by sdushantha python
2129 MIT
Quickly fetch your WiFi password and if needed, generate a QR code of your WiFi to allow phones to easily connect
by KasperskyLab python
1870 NOASSERTION
TinyCheck allows you to easily capture network communications from a smartphone or any device which can be associated to a Wi-Fi access point in order to quickly analyze them. This can be used to check if any suspect or malicious communication is outgoing from a smartphone, by using heuristics or specific Indicators of Compromise (IoCs). In order to make it working, you need a computer with a Debian-like operating system and two Wi-Fi interfaces. The best choice is to use a Raspberry Pi (2+) a Wi-Fi dongle and a small touch screen. This tiny configuration (for less than $50) allows you to tap any Wi-Fi device, anywhere.
by OpenIntelWireless swift
899 BSD-3-Clause
Intel Wi-Fi Client for itlwm
by seemoo-lab python
641 GPL-3.0
An open source implementation of Apple's Wi-Fi Password Sharing protocol in Python.
by AppleIntelWifi c
495 NOASSERTION
Kext providing initial support for Intel wireless devices
by pucherot javascript
337 GPL-3.0
WIFI / LAN intruder detector. Check the devices connected and alert you with unknown devices. It also warns of the disconnection of "always connected" devices
by MS-WEB-BN shell
214
Automated WPA/WPA2 PSK attack tool.
by aircrack-ng c
200 NOASSERTION
Realtek rtl8814au driver
Top Authors in Wifi
1
10 Libraries
882
2
6 Libraries
27
3
6 Libraries
2407
4
6 Libraries
14
5
5 Libraries
31
6
5 Libraries
801
7
5 Libraries
650
8
5 Libraries
432
9
5 Libraries
67
10
5 Libraries
56
1
10 Libraries
882
2
6 Libraries
27
3
6 Libraries
2407
4
6 Libraries
14
5
5 Libraries
31
6
5 Libraries
801
7
5 Libraries
650
8
5 Libraries
432
9
5 Libraries
67
10
5 Libraries
56
Trending Kits in Wifi
A wifi library is a collection of pre-written code and functions. Developers can use it to interact with wifi networks and devices in their applications. These libraries provide an interface to perform various wifi-related tasks. It includes scanning for networks, connecting to access points, and retrieving network information. It helps in managing network configurations.
In the node.js ecosystem, there are several wifi libraries available. It helps cater to different needs and requirements. These libraries cover various functionalities, including networking, security, access point management, and more.
When exploring these libraries, developers will find various features and capabilities. Some libraries offer straightforward functions for basic wifi interactions. But others provide solutions with encryption protocols, signal-level monitoring, and event systems.
Developers can follow a few tips to find the best wifi library for a specific use case. Reading reviews and feedback can provide insights into the library's reliability and performance. Additionally, comparing the side-by-side features can help make an informed decision.
The usage of a wifi library can vary depending on the project's requirements. It can be as simple as creating a web application. It connects to a wifi network or is as complex as developing an IoT platform. It interacts with many hardware devices over wifi.
Developers should follow the setup instructions provided by the library documentation. They should familiarize themselves with the library's API and understand specific requirements. Additionally, the code samples can help learn how to use its features.
When writing about using a wifi library in a project, you should discuss its purpose and features. Emphasize the need for researching and comparing libraries to find the suitable one. Provide practical advice on using the library and highlight the unique aspects. It will make the chosen library a valuable tool for developers.
wifi-password:
- It helps in retrieving the password of the current wifi network.
- It supports accessing the stored wifi password from the operating system.
- Wifi password retrieval applications, network security tools, and wifi credential management.
node-wifi:
- It helps in managing wifi networks programmatically.
- It supports connecting to wifi networks and scanning for available networks. It helps in getting the current connection status.
- IoT devices, automated network management systems, and network monitoring tools.
wireless-tools:
- It helps in interacting with wireless network interfaces.
- It supports scanning for networks, connecting to networks, and configuring wireless settings.
- Network configuration utilities, wireless network management tools, and wifi hotspot management.
wifi-location:
- It helps determine a wifi network's location based on its BSSID.
- It supports mapping BSSIDs to physical locations using wifi geolocation databases.
- Wi-Fi-based geolocation services, asset tracking systems, and location-aware applications.
wifi-name:
- It helps in getting the current SSID of the connected wifi network.
- It supports retrieving the network name to which the device is currently connected.
- Wifi status indicators, network monitoring applications, and wifi profile management.
node-wifi-scanner:
- It helps in scanning and discovering wifi networks in the vicinity.
- It supports retrieving information about available networks. It includes SSID, signal strength, and encryption type.
- Wifi analyzer tools, network mapping applications, and location-based services.
node-wifi-tools:
- It helps in managing and troubleshooting wifi connections.
- It supports features like getting network information. It helps in connecting to networks and performing network diagnostics.
- Wifi troubleshooting utilities, network monitoring applications, wifi connection managers.
wifi-ap:
- It helps create a wifi access point (AP) using a Node.js application.
- It supports configuring and managing a software-based wifi access point.
- Wifi hotspot applications, wireless network simulation, IoT device provisioning.
FAQ
1. What is the nodejs wifi library, and how does it work?
The nodejs wifi library is a collection of code and functions. It enables developers to interact with wifi networks and devices using Node.js. It provides an interface to perform various wifi-related tasks. These tasks can be scanning for available networks and connecting to access points. It also helps in retrieving network information and managing network configurations. The library works by utilizing the underlying operating system's wifi capabilities. It provides a simplified API for developers to work with.
2. How do you use the wifi interface with this library?
The wifi interface allows developers to interact with wifi networks and devices. To use the wifi interface, developers can use the functions to perform scanning. It also helps connect to a specific network and disconnect from a network. It helps in retrieving information about the current network connection. Developers can manage wifi interactions within their applications by using the wifi interface.
3. What type of JavaScript runtime environment is required to use the wifi library?
The nodejs wifi library is compatible with any JavaScript runtime environment. Node.js is a runtime built on Chrome's V8 JavaScript engine. It allows JavaScript code on the server side. To use the nodejs wifi library, developers must have a runtime environment. It supports Node.js installed on its system.
4. How can access points be configured using the nodejs wifi library?
With the nodejs wifi library, access points can be configured. Developers can use the library's functions to connect to a specific access point. It provides the necessary credentials or security information. Additionally, the library allows for managing and manipulating access point configurations. It includes changing the network name (SSID), password, encryption protocols, and other settings. This flexibility enables developers to automate access point configurations within their applications.
5. Is it possible to get a remote IP address from a remote client using this library?
Yes, it is possible to retrieve a remote client's IP address using the nodejs wifi library. Developers can connect with a remote client over wifi by utilizing the functions. It helps retrieve information about the client, including the remote IP address. This can be useful for various applications, such as tracking client connections. It helps enforce access restrictions or analyze network traffic patterns.
6. What are the advantages of using WebSocket with the wifi library over other protocols?
WebSocket is a communication protocol. It offers full-duplex communication channels over a single TCP connection. WebSocket offers several advantages over other protocols when used with the wifi library. WebSocket enables real-time, bidirectional communication between the client and the server. It allows instant data updates and notifications. Additionally, WebSocket connections have a lower overhead compared to traditional HTTP connections. It results in reduced latency and improved performance.
Furthermore, WebSocket supports persistent connections, eliminating the need for repeated connection establishment. It can be beneficial for scenarios involving frequent data transmission and real-time updates. WebSocket enhances the capabilities of the nodejs wifi library. It provides a reliable and efficient communication channel for wifi-enabled applications.
Trending Discussions on Wifi
Puppeteer scrape value generated in javaScript
Android Studio BumbleBee pair wifi not working
Android Studio Bumblebee Wifi pairing Issue
Is the Android Wifi-API really so broken on Android 10+?
Should macOS daemons be made from the "Command Line Tool" Xcode template?
How to rebuild epoll package in electron?
How to get a response from the async write function in capacitor-community / bluetooth-le
Android Studio Disconnects From Physical Device
Winsock sendto returns error 10049 (WSAEADDRNOTAVAIL) for broadcast address after network adapter is disabled or physically disconnected
Compress & Upload large videos to Google cloud storage using Flutter/Dart
QUESTION
Puppeteer scrape value generated in javaScript
Asked 2022-Apr-17 at 10:16How do I scrape a value that is generated within Javascript.
I have been trying to figure this out for a few days and now I'm stuck. I have the page login stuff working.
The page looks like this in a browser and I want to extract the SoC% value and nothing else. In this example the value is 92.16%
This page will auto update every 10 minute.
I can see the part of the JS that returns the value but I don't know how to scrape this value into a variable in my script.
1if ('battery_soc' in d.last) {
2 content+="<td>"+d.last.battery_soc+"%</td>";
3}
4else {
5 content+="<td class='hidden-xs'>&mdash;</td>";
6}
Here is the full html page if that helps.
1if ('battery_soc' in d.last) {
2 content+="<td>"+d.last.battery_soc+"%</td>";
3}
4else {
5 content+="<td class='hidden-xs'>&mdash;</td>";
6}<!DOCTYPE html>
7<html>
8 <head>
9 <meta charset="utf-8">
10 <meta http-equiv="X-UA-Compatible" content="IE=9;IE=10;IE=Edge,chrome=1" />
11 <meta name="viewport" content="width=device-width, initial-scale=1" />
12 <meta name="Description" content="Select.Live is web based SCADA for SP Pro inverters and its system" />
13 <meta name="Author" content="Selctronic Australia Pty Ltd" />
14 <title>Select.Live Portal | Selectronic Australia</title>
15 <link href="https://fonts.googleapis.com/css?family=Titillium+Web" rel="stylesheet">
16 <link href="/css/bootstrap.min.css" rel="stylesheet" />
17 <link href="/css/zebra_datepicker.min.css" rel="stylesheet" />
18 <link href="/css/ad-style.css" rel="stylesheet" />
19 <script src="/js/jquery.min.js"></script>
20 <script src="/js/bootstrap.min.js"></script>
21 <script src="/js/zebra_datepicker.min.js"></script>
22 <script src="/js/user_geolocation.js"></script>
23 <script type="text/javascript">
24 var geocodeKey = "";
25 </script>
26 </head>
27
28 <body>
29 <div class="ad-header">
30 <button type="button" class="side-toggle left hidden-sm hidden-md hidden-lg" data-toggle="open">
31 <span class="bar"></span> <span class="bar"></span> <span class="bar"></span>
32 </button>
33 <div class="header-logo left"></div>
34 <div class="clear"></div>
35 </div>
36
37
38<div class="section">
39
40 <div align="center" class="side-menu">
41 <ul>
42 <li><a href="/systems"><span class="glyphicon glyphicon-list"></span><span class="hideit">Systems</span></a></li>
43 <li><a href="/myprofile"><span class="glyphicon glyphicon-user"></span><span class="hideit">My Profile</span></a></li>
44 <li><a href="/logout"><span class="glyphicon glyphicon-log-out"></span><span class="hideit">Logout</span></a></li>
45 </ul>
46 </div>
47 <script type="text/javascript">
48 $(".side-menu li:nth-child(1)").addClass("active");
49 </script>
50
51
52 <div class="main-content container-fluid">
53 <div class="row">
54 <div id="map" style="height: 300px;"></div>
55 </div>
56
57 <!-- My Systems -->
58 <div class="row">
59 <div class="col col-md-12 main-content-padding">
60 <div class="main-content-inner">
61 <div class="main-content-header">
62 <h3>My Systems</h3>
63 </div>
64 <div class="main-content-body">
65 <table id="ownerSystems" class="table table-hover table-responsive table-striped">
66 <thead>
67 <tr>
68 <th>System Name</th>
69 <th>Status</th>
70 <th>SoC</th>
71 <th class="hidden-xs">Production</th>
72 <th class="hidden-xs">Purchased</th>
73 <th class="hidden-xs">Consumption</th>
74 </tr>
75 </thead>
76 <tbody>
77 </tbody>
78 </table>
79 <div align="right"> <a href="#" class="add_system btn btn-primary btn-lg"><span class="glyphicon glyphicon-plus"></span> Add a System</a> </div>
80 </div>
81 </div>
82 </div>
83 </div>
84
85 <!-- Other systems (have installer access to these) 0 -->
86 <div id="otherSystems" class="row hidden" >
87<div class="col col-md-12 main-content-padding">
88 <div class="main-content-inner">
89 <div class="main-content-header">
90 <h3>Other Systems</h3>
91 </div>
92 <div class="main-content-body">
93 <table id="installerSystems" class="table table-hover table-responsive table-striped">
94 <thead>
95 <tr>
96 <th>System Name</th>
97 <th>Status</th>
98 <th>SoC</th>
99 <th class="hidden-xs">Production</th>
100 <th class="hidden-xs">Purchased</th>
101 <th class="hidden-xs">Consumption</th>
102 </tr>
103 </thead>
104 <tbody>
105 </tbody>
106 </table>
107 </div>
108 </div>
109 </div>
110 </div>
111
112 <div class="clear"></div>
113 </div>
114</div>
115<div class="overlay">
116 <div class="container">
117 <div class="row">
118 <div class="col-sm-12">
119 <div class="overlay_content" style="overflow-y: auto;">
120 <div class="overlay_header">
121 <div class="right"> <a href="#" class="overlay_close"><span class="glyphicon glyphicon-remove"></span></a> </div>
122 <div class="clearfix"></div>
123 </div>
124 <div class="overlay_body">
125 <h3>Add a new System to your profile</h3>
126 <p>Connect your Select.Live Device to your SP PRO and set it up so that it is connected to the Internet.</p>
127 <p>Please find the Device ID and Serial number on the LCD screen of your Select.Live Device as shown in the example,
128 and copy those details into the form below.
129 </p>
130 <img src="images/LCD_claim_Selectronic.png">
131 <form id="claim_form" style="clear:both;">
132 <div class="form-group">
133 <label for="claim_code">Device ID</label>
134 <input type="text" id="claim_code" class="form-control" name="devhash" placeholder="id">
135 </div>
136 <div class="form-group">
137 <label for="claim_serial">Serial</label>
138 <input type="text" id="claim_serial" class="form-control" name="serialnum" placeholder="serial number">
139 </div>
140 <div class="form-group">
141 <label for="claim_type">Access Required</label><br>
142 <label class="radio-inline"><input type="radio" name="claim_type" value="owner" checked> Owner</label>
143 <label class="radio-inline"><input type="radio" name="claim_type" value="installer"> Installer</label>
144 </div>
145 <button type="button" id="add_claim" class="btn btn-primary" value="Add">Add System</button>
146 </form>
147 <br /><br /><br />
148 <div id="claim_failed"></div>
149 </div>
150 </div>
151 </div>
152 </div>
153 </div>
154</div>
155<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDl070Qq1sR3HnNr3LegChHPV8c7WWjZM4"></script>
156<script type="text/javascript">
157 var map = new google.maps.Map(document.getElementById('map'), {
158 zoom: 10,
159 center: {lat: -37.7621346, lng: 145.3132782 },
160 gestureHandling: 'cooperative',
161 streetViewControl: false,
162 fullscreenControl: false
163 });
164 var getData=function(installer) {
165 $.ajax({
166 url:'systems/list'+(installer ? '/installer' : '/owner'),
167 type:'get',
168 dataType: 'json',
169 cache: false,
170 success:function(data) {
171 var tableID = data.installer ? 'installerSystems' : 'ownerSystems';
172 // data=JSON.parse(data);
173 if(data.systems.length) {
174 var content="";
175 var time_diff;
176 data.systems.forEach(function(d) {
177 content+="<tr onmouseover='zoom("+d.lat+","+d.lng+")' onmouseout='zoomout()'>";
178 content+="<td><a href='/dashboard/"+d.did+"'>"+d.name+"</a></td>";
179 content+="<td>";
180 var con_stat="<span class='glyphicon glyphicon-ok-sign color-green s-large' style='vertical-align:middle;'></span> ";
181 if (d.events) con_stat="<span class='glyphicon glyphicon-exclamation-sign color-yellow s-large' style='vertical-align:middle;'></span> ";
182 if (d.last) {
183 time_diff = d.delta_ts;
184 console.log("time_diff="+time_diff);
185 if(time_diff<60) {
186 con_stat+="<span>"+Math.round(time_diff)+" seconds ago</span>";
187 }
188 else if(time_diff<1200) {
189 con_stat+="<span>"+Math.round(time_diff/60)+" minutes ago</span>";
190 }
191 else if(time_diff<3600) {
192 con_stat="<span class='glyphicon glyphicon-remove-sign color-red s-large' style='vertical-align:middle;'></span> ";
193 con_stat+="<span>"+Math.round(time_diff/60)+" minutes ago</span>";
194 }
195 else if(time_diff<86400) {
196 con_stat="<span class='glyphicon glyphicon-remove-sign color-red s-large' style='vertical-align:middle;'></span> ";
197 con_stat+="<span>"+Math.round(time_diff/3600)+" hours ago</span>";
198 }
199 else {
200 con_stat="<span class='glyphicon glyphicon-remove-sign color-red s-large' style='vertical-align:middle;'></span> ";
201 con_stat+="<span>"+Math.round(time_diff/86400)+" days ago</span>";
202 }
203 content+=con_stat;
204 content+="</td>";
205 if ('battery_soc' in d.last) {
206 content+="<td>"+d.last.battery_soc+"%</td>";
207 }
208 else {
209 content+="<td class='hidden-xs'>&mdash;</td>";
210 }
211 if ('solar_wh_total' in d.last) {
212 content+="<td class='hidden-xs'>"+d.last.solar_wh_total.toFixed()+" kWh</td>";
213 }
214 else {
215 content+="<td class='hidden-xs'>&mdash;</td>";
216 }
217 if ('grid_in_wh_total' in d.last) {
218 content+="<td class='hidden-xs'>"+d.last.grid_in_wh_total.toFixed()+" kWh</td>";
219 }
220 else {
221 content+="<td class='hidden-xs'>&mdash;</td>";
222 }
223 if ('load_wh_total' in d.last) {
224 content+="<td class='hidden-xs'>"+d.last.load_wh_total.toFixed()+" kWh</td>";
225 }
226 else {
227 content+="<td class='hidden-xs'>&mdash;</td>";
228 }
229 }
230 else {
231 content += "<span class='glyphicon glyphicon-remove-sign color-red s-large' style='vertical-align:middle;'></span>";
232 content += "<span>No Measurements Recorded</span></td>";
233 content += "<td>&mdash;</td><td class='hidden-xs'>&mdash;</td><td class='hidden-xs'>&mdash;</td><td class='hidden-xs'>&mdash;</td>";
234 }
235 content+="</tr>";
236 var marker=new google.maps.Marker({position: {lat: d.lat, lng: d.lng}, map: map, title:d.name});
237 });
238 $('table#'+tableID+' tbody').html(content);
239 if (data.installer) $('div#otherSystems.hidden').removeClass('hidden');
240 }
241 else if (!data.installer) {
242 $('table#'+tableID+' tbody').html("<tr><td colspan='7'>You don't have any SP Pro Systems</td></tr>");
243 }
244 }
245 });
246 }
247 var addSystem=function() {
248 var serial = $('#claim_serial').val();
249 var code = $('#claim_code').val();
250 var access = $('input[name=claim_type]:checked').val();
251 console.log('Attempting to claim with serial='+serial+', code='+code+', access='+access);
252 $.ajax({
253 url:'systems/claim',
254 type:'post',
255 data: { code: code, serial: serial, access: access },
256 dataType: 'json',
257 cache: false
258 })
259 .done(function(data) {
260 var is_installer = (data.access == 'installer') ? true : false;
261 $('.overlay').hide();
262 $('div#claim_failed').html('');
263 $('form#claim_form input').val('');
264 getData(is_installer);
265 })
266 .fail(function(data) {
267 if (data.responseJSON.reason.match(/No matching inverter/i)) {
268 $('div#claim_failed').html('<p>&nbsp;</p><h3 class="text-danger">Could not find a matching inverter</h3>'
269 +'<p class="text-danger">Please check the following to fix this error:</p>'
270 +'<ul class="text-danger" style="padding-left:20px;">'
271 +'<li>Check that the Select.Live Device is powered on with text visible on the screen'
272 +'<li>Check that the Select.Live Device screen shows "Cloud: OK" and an IP address'
273 +'<li>If the Select.Live Device screen shows "Cloud: ERROR" or "Cloud: NO LAN":'
274 +'<ul style="padding-left:50px;">'
275 +'<li>for WiFi connection, check your WiFi router is operating correctly, and reset it if necessary'
276 +'<li>for WiFi connection, check that there is a good WiFi signal at your Select.Live device<br>'
277 + '(use your mobile phone or tablet to confirm that the WiFi signal is present)'
278 +'<li>for Ethernet connection, check that the cable is plugged in firmly at both ends;'
279 + ' also check using another device, e.g. a laptop computer, that the cable is working.'
280 +'</ul></ul>'
281 +'<p class="text-danger">If you have checked all the above and still get this error when you '
282 +'attempt to add the system, you will need to reset your Select.Live device and start the '
283 +'setup process again. To reset your Select.Live Device, press and hold the black reset button '
284 +'for 10 seconds.</p>');
285 }
286 else if (data.responseJSON.reason.match(/Access Denied/i)) {
287 $('div#claim_failed').html('<p>&nbsp;</p><h3 class="text-danger">Access denied by owner</h3>'
288 +'<p class="text-danger">The owner of this SP PRO has not given permission for you to have access.</p>'
289 +'<p class="text-danger">Please check you entered the correct <b>Device ID</b> and <b>Serial number</b>.'
290 +' If you think they are correct, you will need to ask the owner of this SP PRO to grant access.</p>');
291 }
292 else if (data.responseJSON.reason.match(/No Owner/i)) {
293 $('div#claim_failed').html('<p>&nbsp;</p><h3 class="text-danger">Access Denied</h3>'
294 +'<p class="text-danger">Installer access to this SP PRO is not allowed.</p>');
295 }
296 else {
297 $('.overlay').hide();
298 alert(data.responseJSON.reason);
299 }
300 });
301 };
302 $('a.add_system').on('click',function(){
303 $('.overlay').show();
304 });
305 $('a.overlay_close').on('click',function(){
306 $('.overlay').hide();
307 });
308 $('#add_claim').on('click',addSystem);
309 $('button.side-toggle').on('click',function(){
310 if($(this).hasClass("openned")) {
311 $(this).removeClass("openned");
312 $(".side-menu").removeClass("in");
313 }
314 else {
315 $(this).addClass("openned");
316 $(".side-menu").addClass("in");
317 }
318 });
319 var zoom=function(x,y) {
320 map.panTo({lat:x,lng: y});
321 map.setZoom(18);
322 }
323 var zoomout=function() {
324 map.setZoom(10);
325 }
326 $(window).ready(function() {
327 getData(false);
328 });
329 $(window).resize(function(){
330 });
331</script>
332
333 </body>
334</html>
Here is my script so far
1if ('battery_soc' in d.last) {
2 content+="<td>"+d.last.battery_soc+"%</td>";
3}
4else {
5 content+="<td class='hidden-xs'>&mdash;</td>";
6}<!DOCTYPE html>
7<html>
8 <head>
9 <meta charset="utf-8">
10 <meta http-equiv="X-UA-Compatible" content="IE=9;IE=10;IE=Edge,chrome=1" />
11 <meta name="viewport" content="width=device-width, initial-scale=1" />
12 <meta name="Description" content="Select.Live is web based SCADA for SP Pro inverters and its system" />
13 <meta name="Author" content="Selctronic Australia Pty Ltd" />
14 <title>Select.Live Portal | Selectronic Australia</title>
15 <link href="https://fonts.googleapis.com/css?family=Titillium+Web" rel="stylesheet">
16 <link href="/css/bootstrap.min.css" rel="stylesheet" />
17 <link href="/css/zebra_datepicker.min.css" rel="stylesheet" />
18 <link href="/css/ad-style.css" rel="stylesheet" />
19 <script src="/js/jquery.min.js"></script>
20 <script src="/js/bootstrap.min.js"></script>
21 <script src="/js/zebra_datepicker.min.js"></script>
22 <script src="/js/user_geolocation.js"></script>
23 <script type="text/javascript">
24 var geocodeKey = "";
25 </script>
26 </head>
27
28 <body>
29 <div class="ad-header">
30 <button type="button" class="side-toggle left hidden-sm hidden-md hidden-lg" data-toggle="open">
31 <span class="bar"></span> <span class="bar"></span> <span class="bar"></span>
32 </button>
33 <div class="header-logo left"></div>
34 <div class="clear"></div>
35 </div>
36
37
38<div class="section">
39
40 <div align="center" class="side-menu">
41 <ul>
42 <li><a href="/systems"><span class="glyphicon glyphicon-list"></span><span class="hideit">Systems</span></a></li>
43 <li><a href="/myprofile"><span class="glyphicon glyphicon-user"></span><span class="hideit">My Profile</span></a></li>
44 <li><a href="/logout"><span class="glyphicon glyphicon-log-out"></span><span class="hideit">Logout</span></a></li>
45 </ul>
46 </div>
47 <script type="text/javascript">
48 $(".side-menu li:nth-child(1)").addClass("active");
49 </script>
50
51
52 <div class="main-content container-fluid">
53 <div class="row">
54 <div id="map" style="height: 300px;"></div>
55 </div>
56
57 <!-- My Systems -->
58 <div class="row">
59 <div class="col col-md-12 main-content-padding">
60 <div class="main-content-inner">
61 <div class="main-content-header">
62 <h3>My Systems</h3>
63 </div>
64 <div class="main-content-body">
65 <table id="ownerSystems" class="table table-hover table-responsive table-striped">
66 <thead>
67 <tr>
68 <th>System Name</th>
69 <th>Status</th>
70 <th>SoC</th>
71 <th class="hidden-xs">Production</th>
72 <th class="hidden-xs">Purchased</th>
73 <th class="hidden-xs">Consumption</th>
74 </tr>
75 </thead>
76 <tbody>
77 </tbody>
78 </table>
79 <div align="right"> <a href="#" class="add_system btn btn-primary btn-lg"><span class="glyphicon glyphicon-plus"></span> Add a System</a> </div>
80 </div>
81 </div>
82 </div>
83 </div>
84
85 <!-- Other systems (have installer access to these) 0 -->
86 <div id="otherSystems" class="row hidden" >
87<div class="col col-md-12 main-content-padding">
88 <div class="main-content-inner">
89 <div class="main-content-header">
90 <h3>Other Systems</h3>
91 </div>
92 <div class="main-content-body">
93 <table id="installerSystems" class="table table-hover table-responsive table-striped">
94 <thead>
95 <tr>
96 <th>System Name</th>
97 <th>Status</th>
98 <th>SoC</th>
99 <th class="hidden-xs">Production</th>
100 <th class="hidden-xs">Purchased</th>
101 <th class="hidden-xs">Consumption</th>
102 </tr>
103 </thead>
104 <tbody>
105 </tbody>
106 </table>
107 </div>
108 </div>
109 </div>
110 </div>
111
112 <div class="clear"></div>
113 </div>
114</div>
115<div class="overlay">
116 <div class="container">
117 <div class="row">
118 <div class="col-sm-12">
119 <div class="overlay_content" style="overflow-y: auto;">
120 <div class="overlay_header">
121 <div class="right"> <a href="#" class="overlay_close"><span class="glyphicon glyphicon-remove"></span></a> </div>
122 <div class="clearfix"></div>
123 </div>
124 <div class="overlay_body">
125 <h3>Add a new System to your profile</h3>
126 <p>Connect your Select.Live Device to your SP PRO and set it up so that it is connected to the Internet.</p>
127 <p>Please find the Device ID and Serial number on the LCD screen of your Select.Live Device as shown in the example,
128 and copy those details into the form below.
129 </p>
130 <img src="images/LCD_claim_Selectronic.png">
131 <form id="claim_form" style="clear:both;">
132 <div class="form-group">
133 <label for="claim_code">Device ID</label>
134 <input type="text" id="claim_code" class="form-control" name="devhash" placeholder="id">
135 </div>
136 <div class="form-group">
137 <label for="claim_serial">Serial</label>
138 <input type="text" id="claim_serial" class="form-control" name="serialnum" placeholder="serial number">
139 </div>
140 <div class="form-group">
141 <label for="claim_type">Access Required</label><br>
142 <label class="radio-inline"><input type="radio" name="claim_type" value="owner" checked> Owner</label>
143 <label class="radio-inline"><input type="radio" name="claim_type" value="installer"> Installer</label>
144 </div>
145 <button type="button" id="add_claim" class="btn btn-primary" value="Add">Add System</button>
146 </form>
147 <br /><br /><br />
148 <div id="claim_failed"></div>
149 </div>
150 </div>
151 </div>
152 </div>
153 </div>
154</div>
155<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDl070Qq1sR3HnNr3LegChHPV8c7WWjZM4"></script>
156<script type="text/javascript">
157 var map = new google.maps.Map(document.getElementById('map'), {
158 zoom: 10,
159 center: {lat: -37.7621346, lng: 145.3132782 },
160 gestureHandling: 'cooperative',
161 streetViewControl: false,
162 fullscreenControl: false
163 });
164 var getData=function(installer) {
165 $.ajax({
166 url:'systems/list'+(installer ? '/installer' : '/owner'),
167 type:'get',
168 dataType: 'json',
169 cache: false,
170 success:function(data) {
171 var tableID = data.installer ? 'installerSystems' : 'ownerSystems';
172 // data=JSON.parse(data);
173 if(data.systems.length) {
174 var content="";
175 var time_diff;
176 data.systems.forEach(function(d) {
177 content+="<tr onmouseover='zoom("+d.lat+","+d.lng+")' onmouseout='zoomout()'>";
178 content+="<td><a href='/dashboard/"+d.did+"'>"+d.name+"</a></td>";
179 content+="<td>";
180 var con_stat="<span class='glyphicon glyphicon-ok-sign color-green s-large' style='vertical-align:middle;'></span> ";
181 if (d.events) con_stat="<span class='glyphicon glyphicon-exclamation-sign color-yellow s-large' style='vertical-align:middle;'></span> ";
182 if (d.last) {
183 time_diff = d.delta_ts;
184 console.log("time_diff="+time_diff);
185 if(time_diff<60) {
186 con_stat+="<span>"+Math.round(time_diff)+" seconds ago</span>";
187 }
188 else if(time_diff<1200) {
189 con_stat+="<span>"+Math.round(time_diff/60)+" minutes ago</span>";
190 }
191 else if(time_diff<3600) {
192 con_stat="<span class='glyphicon glyphicon-remove-sign color-red s-large' style='vertical-align:middle;'></span> ";
193 con_stat+="<span>"+Math.round(time_diff/60)+" minutes ago</span>";
194 }
195 else if(time_diff<86400) {
196 con_stat="<span class='glyphicon glyphicon-remove-sign color-red s-large' style='vertical-align:middle;'></span> ";
197 con_stat+="<span>"+Math.round(time_diff/3600)+" hours ago</span>";
198 }
199 else {
200 con_stat="<span class='glyphicon glyphicon-remove-sign color-red s-large' style='vertical-align:middle;'></span> ";
201 con_stat+="<span>"+Math.round(time_diff/86400)+" days ago</span>";
202 }
203 content+=con_stat;
204 content+="</td>";
205 if ('battery_soc' in d.last) {
206 content+="<td>"+d.last.battery_soc+"%</td>";
207 }
208 else {
209 content+="<td class='hidden-xs'>&mdash;</td>";
210 }
211 if ('solar_wh_total' in d.last) {
212 content+="<td class='hidden-xs'>"+d.last.solar_wh_total.toFixed()+" kWh</td>";
213 }
214 else {
215 content+="<td class='hidden-xs'>&mdash;</td>";
216 }
217 if ('grid_in_wh_total' in d.last) {
218 content+="<td class='hidden-xs'>"+d.last.grid_in_wh_total.toFixed()+" kWh</td>";
219 }
220 else {
221 content+="<td class='hidden-xs'>&mdash;</td>";
222 }
223 if ('load_wh_total' in d.last) {
224 content+="<td class='hidden-xs'>"+d.last.load_wh_total.toFixed()+" kWh</td>";
225 }
226 else {
227 content+="<td class='hidden-xs'>&mdash;</td>";
228 }
229 }
230 else {
231 content += "<span class='glyphicon glyphicon-remove-sign color-red s-large' style='vertical-align:middle;'></span>";
232 content += "<span>No Measurements Recorded</span></td>";
233 content += "<td>&mdash;</td><td class='hidden-xs'>&mdash;</td><td class='hidden-xs'>&mdash;</td><td class='hidden-xs'>&mdash;</td>";
234 }
235 content+="</tr>";
236 var marker=new google.maps.Marker({position: {lat: d.lat, lng: d.lng}, map: map, title:d.name});
237 });
238 $('table#'+tableID+' tbody').html(content);
239 if (data.installer) $('div#otherSystems.hidden').removeClass('hidden');
240 }
241 else if (!data.installer) {
242 $('table#'+tableID+' tbody').html("<tr><td colspan='7'>You don't have any SP Pro Systems</td></tr>");
243 }
244 }
245 });
246 }
247 var addSystem=function() {
248 var serial = $('#claim_serial').val();
249 var code = $('#claim_code').val();
250 var access = $('input[name=claim_type]:checked').val();
251 console.log('Attempting to claim with serial='+serial+', code='+code+', access='+access);
252 $.ajax({
253 url:'systems/claim',
254 type:'post',
255 data: { code: code, serial: serial, access: access },
256 dataType: 'json',
257 cache: false
258 })
259 .done(function(data) {
260 var is_installer = (data.access == 'installer') ? true : false;
261 $('.overlay').hide();
262 $('div#claim_failed').html('');
263 $('form#claim_form input').val('');
264 getData(is_installer);
265 })
266 .fail(function(data) {
267 if (data.responseJSON.reason.match(/No matching inverter/i)) {
268 $('div#claim_failed').html('<p>&nbsp;</p><h3 class="text-danger">Could not find a matching inverter</h3>'
269 +'<p class="text-danger">Please check the following to fix this error:</p>'
270 +'<ul class="text-danger" style="padding-left:20px;">'
271 +'<li>Check that the Select.Live Device is powered on with text visible on the screen'
272 +'<li>Check that the Select.Live Device screen shows "Cloud: OK" and an IP address'
273 +'<li>If the Select.Live Device screen shows "Cloud: ERROR" or "Cloud: NO LAN":'
274 +'<ul style="padding-left:50px;">'
275 +'<li>for WiFi connection, check your WiFi router is operating correctly, and reset it if necessary'
276 +'<li>for WiFi connection, check that there is a good WiFi signal at your Select.Live device<br>'
277 + '(use your mobile phone or tablet to confirm that the WiFi signal is present)'
278 +'<li>for Ethernet connection, check that the cable is plugged in firmly at both ends;'
279 + ' also check using another device, e.g. a laptop computer, that the cable is working.'
280 +'</ul></ul>'
281 +'<p class="text-danger">If you have checked all the above and still get this error when you '
282 +'attempt to add the system, you will need to reset your Select.Live device and start the '
283 +'setup process again. To reset your Select.Live Device, press and hold the black reset button '
284 +'for 10 seconds.</p>');
285 }
286 else if (data.responseJSON.reason.match(/Access Denied/i)) {
287 $('div#claim_failed').html('<p>&nbsp;</p><h3 class="text-danger">Access denied by owner</h3>'
288 +'<p class="text-danger">The owner of this SP PRO has not given permission for you to have access.</p>'
289 +'<p class="text-danger">Please check you entered the correct <b>Device ID</b> and <b>Serial number</b>.'
290 +' If you think they are correct, you will need to ask the owner of this SP PRO to grant access.</p>');
291 }
292 else if (data.responseJSON.reason.match(/No Owner/i)) {
293 $('div#claim_failed').html('<p>&nbsp;</p><h3 class="text-danger">Access Denied</h3>'
294 +'<p class="text-danger">Installer access to this SP PRO is not allowed.</p>');
295 }
296 else {
297 $('.overlay').hide();
298 alert(data.responseJSON.reason);
299 }
300 });
301 };
302 $('a.add_system').on('click',function(){
303 $('.overlay').show();
304 });
305 $('a.overlay_close').on('click',function(){
306 $('.overlay').hide();
307 });
308 $('#add_claim').on('click',addSystem);
309 $('button.side-toggle').on('click',function(){
310 if($(this).hasClass("openned")) {
311 $(this).removeClass("openned");
312 $(".side-menu").removeClass("in");
313 }
314 else {
315 $(this).addClass("openned");
316 $(".side-menu").addClass("in");
317 }
318 });
319 var zoom=function(x,y) {
320 map.panTo({lat:x,lng: y});
321 map.setZoom(18);
322 }
323 var zoomout=function() {
324 map.setZoom(10);
325 }
326 $(window).ready(function() {
327 getData(false);
328 });
329 $(window).resize(function(){
330 });
331</script>
332
333 </body>
334</html>const scrapePersons = async () => {
335 // import launchChrome and newPage from the browser.js file in the same directory
336 const { launchChrome } = require("./browser");
337
338 // Flow 1 => Launching chrome and opening a new tab/page
339 const [newPage, exitChrome] = await launchChrome();
340 const [page] = await newPage();
341
342 const emailSelector="input[name=email]";
343 const pwdSelector="input[name=pwd]";
344 const btnSelector=".btn";
345
346 // exit the function if the tab is not properly opened
347 if (!page) return;
348
349 // Flow 2 => Visiting a website's home page
350 const url = "https://select.live/";
351 console.log("Opening " + url);
352 try {
353 await page.goto(url, {
354 waitUntil: "networkidle0", // wait till all network requests has been processed
355 });
356 } catch(e) {
357 console.error("Unable to visit " + url, e);
358 await exitChrome(); // close chrome on error
359 return; // exiting the function
360 }
361
362 //Perform the Login
363 await page.waitForSelector(emailSelector);
364 console.log('40 Found name="email" on page');
365
366 await page.waitForSelector(pwdSelector);
367 console.log('50 Found name="pwd" on page');
368
369 await page.waitForSelector(btnSelector);
370 console.log('55 Found the button with class namne btn');
371
372 await page.type(emailSelector, 'Username Goes Here');
373 await page.type(pwdSelector, 'Password Goes Here');
374 console.log('60 Entered email and password');
375
376 //Click the Login Butotn
377 try{
378 await page.click(btnSelector);
379 console.log('70 Clicked the Login Button');
380 }
381 catch(e){
382 console.error('Unable to click the login button' + btnSelector + ' ', e)
383 }
384
385 // Find the Power Percentage Value
386
387
388 await exitChrome(); // close chrome
389 console.log('900 Exited Chrome')
390};
391
392module.exports = scrapePersons;
ANSWER
Answered 2022-Apr-17 at 10:16try waiting for table cell to be rendered with page.waitForSelector:
1if ('battery_soc' in d.last) {
2 content+="<td>"+d.last.battery_soc+"%</td>";
3}
4else {
5 content+="<td class='hidden-xs'>&mdash;</td>";
6}<!DOCTYPE html>
7<html>
8 <head>
9 <meta charset="utf-8">
10 <meta http-equiv="X-UA-Compatible" content="IE=9;IE=10;IE=Edge,chrome=1" />
11 <meta name="viewport" content="width=device-width, initial-scale=1" />
12 <meta name="Description" content="Select.Live is web based SCADA for SP Pro inverters and its system" />
13 <meta name="Author" content="Selctronic Australia Pty Ltd" />
14 <title>Select.Live Portal | Selectronic Australia</title>
15 <link href="https://fonts.googleapis.com/css?family=Titillium+Web" rel="stylesheet">
16 <link href="/css/bootstrap.min.css" rel="stylesheet" />
17 <link href="/css/zebra_datepicker.min.css" rel="stylesheet" />
18 <link href="/css/ad-style.css" rel="stylesheet" />
19 <script src="/js/jquery.min.js"></script>
20 <script src="/js/bootstrap.min.js"></script>
21 <script src="/js/zebra_datepicker.min.js"></script>
22 <script src="/js/user_geolocation.js"></script>
23 <script type="text/javascript">
24 var geocodeKey = "";
25 </script>
26 </head>
27
28 <body>
29 <div class="ad-header">
30 <button type="button" class="side-toggle left hidden-sm hidden-md hidden-lg" data-toggle="open">
31 <span class="bar"></span> <span class="bar"></span> <span class="bar"></span>
32 </button>
33 <div class="header-logo left"></div>
34 <div class="clear"></div>
35 </div>
36
37
38<div class="section">
39
40 <div align="center" class="side-menu">
41 <ul>
42 <li><a href="/systems"><span class="glyphicon glyphicon-list"></span><span class="hideit">Systems</span></a></li>
43 <li><a href="/myprofile"><span class="glyphicon glyphicon-user"></span><span class="hideit">My Profile</span></a></li>
44 <li><a href="/logout"><span class="glyphicon glyphicon-log-out"></span><span class="hideit">Logout</span></a></li>
45 </ul>
46 </div>
47 <script type="text/javascript">
48 $(".side-menu li:nth-child(1)").addClass("active");
49 </script>
50
51
52 <div class="main-content container-fluid">
53 <div class="row">
54 <div id="map" style="height: 300px;"></div>
55 </div>
56
57 <!-- My Systems -->
58 <div class="row">
59 <div class="col col-md-12 main-content-padding">
60 <div class="main-content-inner">
61 <div class="main-content-header">
62 <h3>My Systems</h3>
63 </div>
64 <div class="main-content-body">
65 <table id="ownerSystems" class="table table-hover table-responsive table-striped">
66 <thead>
67 <tr>
68 <th>System Name</th>
69 <th>Status</th>
70 <th>SoC</th>
71 <th class="hidden-xs">Production</th>
72 <th class="hidden-xs">Purchased</th>
73 <th class="hidden-xs">Consumption</th>
74 </tr>
75 </thead>
76 <tbody>
77 </tbody>
78 </table>
79 <div align="right"> <a href="#" class="add_system btn btn-primary btn-lg"><span class="glyphicon glyphicon-plus"></span> Add a System</a> </div>
80 </div>
81 </div>
82 </div>
83 </div>
84
85 <!-- Other systems (have installer access to these) 0 -->
86 <div id="otherSystems" class="row hidden" >
87<div class="col col-md-12 main-content-padding">
88 <div class="main-content-inner">
89 <div class="main-content-header">
90 <h3>Other Systems</h3>
91 </div>
92 <div class="main-content-body">
93 <table id="installerSystems" class="table table-hover table-responsive table-striped">
94 <thead>
95 <tr>
96 <th>System Name</th>
97 <th>Status</th>
98 <th>SoC</th>
99 <th class="hidden-xs">Production</th>
100 <th class="hidden-xs">Purchased</th>
101 <th class="hidden-xs">Consumption</th>
102 </tr>
103 </thead>
104 <tbody>
105 </tbody>
106 </table>
107 </div>
108 </div>
109 </div>
110 </div>
111
112 <div class="clear"></div>
113 </div>
114</div>
115<div class="overlay">
116 <div class="container">
117 <div class="row">
118 <div class="col-sm-12">
119 <div class="overlay_content" style="overflow-y: auto;">
120 <div class="overlay_header">
121 <div class="right"> <a href="#" class="overlay_close"><span class="glyphicon glyphicon-remove"></span></a> </div>
122 <div class="clearfix"></div>
123 </div>
124 <div class="overlay_body">
125 <h3>Add a new System to your profile</h3>
126 <p>Connect your Select.Live Device to your SP PRO and set it up so that it is connected to the Internet.</p>
127 <p>Please find the Device ID and Serial number on the LCD screen of your Select.Live Device as shown in the example,
128 and copy those details into the form below.
129 </p>
130 <img src="images/LCD_claim_Selectronic.png">
131 <form id="claim_form" style="clear:both;">
132 <div class="form-group">
133 <label for="claim_code">Device ID</label>
134 <input type="text" id="claim_code" class="form-control" name="devhash" placeholder="id">
135 </div>
136 <div class="form-group">
137 <label for="claim_serial">Serial</label>
138 <input type="text" id="claim_serial" class="form-control" name="serialnum" placeholder="serial number">
139 </div>
140 <div class="form-group">
141 <label for="claim_type">Access Required</label><br>
142 <label class="radio-inline"><input type="radio" name="claim_type" value="owner" checked> Owner</label>
143 <label class="radio-inline"><input type="radio" name="claim_type" value="installer"> Installer</label>
144 </div>
145 <button type="button" id="add_claim" class="btn btn-primary" value="Add">Add System</button>
146 </form>
147 <br /><br /><br />
148 <div id="claim_failed"></div>
149 </div>
150 </div>
151 </div>
152 </div>
153 </div>
154</div>
155<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDl070Qq1sR3HnNr3LegChHPV8c7WWjZM4"></script>
156<script type="text/javascript">
157 var map = new google.maps.Map(document.getElementById('map'), {
158 zoom: 10,
159 center: {lat: -37.7621346, lng: 145.3132782 },
160 gestureHandling: 'cooperative',
161 streetViewControl: false,
162 fullscreenControl: false
163 });
164 var getData=function(installer) {
165 $.ajax({
166 url:'systems/list'+(installer ? '/installer' : '/owner'),
167 type:'get',
168 dataType: 'json',
169 cache: false,
170 success:function(data) {
171 var tableID = data.installer ? 'installerSystems' : 'ownerSystems';
172 // data=JSON.parse(data);
173 if(data.systems.length) {
174 var content="";
175 var time_diff;
176 data.systems.forEach(function(d) {
177 content+="<tr onmouseover='zoom("+d.lat+","+d.lng+")' onmouseout='zoomout()'>";
178 content+="<td><a href='/dashboard/"+d.did+"'>"+d.name+"</a></td>";
179 content+="<td>";
180 var con_stat="<span class='glyphicon glyphicon-ok-sign color-green s-large' style='vertical-align:middle;'></span> ";
181 if (d.events) con_stat="<span class='glyphicon glyphicon-exclamation-sign color-yellow s-large' style='vertical-align:middle;'></span> ";
182 if (d.last) {
183 time_diff = d.delta_ts;
184 console.log("time_diff="+time_diff);
185 if(time_diff<60) {
186 con_stat+="<span>"+Math.round(time_diff)+" seconds ago</span>";
187 }
188 else if(time_diff<1200) {
189 con_stat+="<span>"+Math.round(time_diff/60)+" minutes ago</span>";
190 }
191 else if(time_diff<3600) {
192 con_stat="<span class='glyphicon glyphicon-remove-sign color-red s-large' style='vertical-align:middle;'></span> ";
193 con_stat+="<span>"+Math.round(time_diff/60)+" minutes ago</span>";
194 }
195 else if(time_diff<86400) {
196 con_stat="<span class='glyphicon glyphicon-remove-sign color-red s-large' style='vertical-align:middle;'></span> ";
197 con_stat+="<span>"+Math.round(time_diff/3600)+" hours ago</span>";
198 }
199 else {
200 con_stat="<span class='glyphicon glyphicon-remove-sign color-red s-large' style='vertical-align:middle;'></span> ";
201 con_stat+="<span>"+Math.round(time_diff/86400)+" days ago</span>";
202 }
203 content+=con_stat;
204 content+="</td>";
205 if ('battery_soc' in d.last) {
206 content+="<td>"+d.last.battery_soc+"%</td>";
207 }
208 else {
209 content+="<td class='hidden-xs'>&mdash;</td>";
210 }
211 if ('solar_wh_total' in d.last) {
212 content+="<td class='hidden-xs'>"+d.last.solar_wh_total.toFixed()+" kWh</td>";
213 }
214 else {
215 content+="<td class='hidden-xs'>&mdash;</td>";
216 }
217 if ('grid_in_wh_total' in d.last) {
218 content+="<td class='hidden-xs'>"+d.last.grid_in_wh_total.toFixed()+" kWh</td>";
219 }
220 else {
221 content+="<td class='hidden-xs'>&mdash;</td>";
222 }
223 if ('load_wh_total' in d.last) {
224 content+="<td class='hidden-xs'>"+d.last.load_wh_total.toFixed()+" kWh</td>";
225 }
226 else {
227 content+="<td class='hidden-xs'>&mdash;</td>";
228 }
229 }
230 else {
231 content += "<span class='glyphicon glyphicon-remove-sign color-red s-large' style='vertical-align:middle;'></span>";
232 content += "<span>No Measurements Recorded</span></td>";
233 content += "<td>&mdash;</td><td class='hidden-xs'>&mdash;</td><td class='hidden-xs'>&mdash;</td><td class='hidden-xs'>&mdash;</td>";
234 }
235 content+="</tr>";
236 var marker=new google.maps.Marker({position: {lat: d.lat, lng: d.lng}, map: map, title:d.name});
237 });
238 $('table#'+tableID+' tbody').html(content);
239 if (data.installer) $('div#otherSystems.hidden').removeClass('hidden');
240 }
241 else if (!data.installer) {
242 $('table#'+tableID+' tbody').html("<tr><td colspan='7'>You don't have any SP Pro Systems</td></tr>");
243 }
244 }
245 });
246 }
247 var addSystem=function() {
248 var serial = $('#claim_serial').val();
249 var code = $('#claim_code').val();
250 var access = $('input[name=claim_type]:checked').val();
251 console.log('Attempting to claim with serial='+serial+', code='+code+', access='+access);
252 $.ajax({
253 url:'systems/claim',
254 type:'post',
255 data: { code: code, serial: serial, access: access },
256 dataType: 'json',
257 cache: false
258 })
259 .done(function(data) {
260 var is_installer = (data.access == 'installer') ? true : false;
261 $('.overlay').hide();
262 $('div#claim_failed').html('');
263 $('form#claim_form input').val('');
264 getData(is_installer);
265 })
266 .fail(function(data) {
267 if (data.responseJSON.reason.match(/No matching inverter/i)) {
268 $('div#claim_failed').html('<p>&nbsp;</p><h3 class="text-danger">Could not find a matching inverter</h3>'
269 +'<p class="text-danger">Please check the following to fix this error:</p>'
270 +'<ul class="text-danger" style="padding-left:20px;">'
271 +'<li>Check that the Select.Live Device is powered on with text visible on the screen'
272 +'<li>Check that the Select.Live Device screen shows "Cloud: OK" and an IP address'
273 +'<li>If the Select.Live Device screen shows "Cloud: ERROR" or "Cloud: NO LAN":'
274 +'<ul style="padding-left:50px;">'
275 +'<li>for WiFi connection, check your WiFi router is operating correctly, and reset it if necessary'
276 +'<li>for WiFi connection, check that there is a good WiFi signal at your Select.Live device<br>'
277 + '(use your mobile phone or tablet to confirm that the WiFi signal is present)'
278 +'<li>for Ethernet connection, check that the cable is plugged in firmly at both ends;'
279 + ' also check using another device, e.g. a laptop computer, that the cable is working.'
280 +'</ul></ul>'
281 +'<p class="text-danger">If you have checked all the above and still get this error when you '
282 +'attempt to add the system, you will need to reset your Select.Live device and start the '
283 +'setup process again. To reset your Select.Live Device, press and hold the black reset button '
284 +'for 10 seconds.</p>');
285 }
286 else if (data.responseJSON.reason.match(/Access Denied/i)) {
287 $('div#claim_failed').html('<p>&nbsp;</p><h3 class="text-danger">Access denied by owner</h3>'
288 +'<p class="text-danger">The owner of this SP PRO has not given permission for you to have access.</p>'
289 +'<p class="text-danger">Please check you entered the correct <b>Device ID</b> and <b>Serial number</b>.'
290 +' If you think they are correct, you will need to ask the owner of this SP PRO to grant access.</p>');
291 }
292 else if (data.responseJSON.reason.match(/No Owner/i)) {
293 $('div#claim_failed').html('<p>&nbsp;</p><h3 class="text-danger">Access Denied</h3>'
294 +'<p class="text-danger">Installer access to this SP PRO is not allowed.</p>');
295 }
296 else {
297 $('.overlay').hide();
298 alert(data.responseJSON.reason);
299 }
300 });
301 };
302 $('a.add_system').on('click',function(){
303 $('.overlay').show();
304 });
305 $('a.overlay_close').on('click',function(){
306 $('.overlay').hide();
307 });
308 $('#add_claim').on('click',addSystem);
309 $('button.side-toggle').on('click',function(){
310 if($(this).hasClass("openned")) {
311 $(this).removeClass("openned");
312 $(".side-menu").removeClass("in");
313 }
314 else {
315 $(this).addClass("openned");
316 $(".side-menu").addClass("in");
317 }
318 });
319 var zoom=function(x,y) {
320 map.panTo({lat:x,lng: y});
321 map.setZoom(18);
322 }
323 var zoomout=function() {
324 map.setZoom(10);
325 }
326 $(window).ready(function() {
327 getData(false);
328 });
329 $(window).resize(function(){
330 });
331</script>
332
333 </body>
334</html>const scrapePersons = async () => {
335 // import launchChrome and newPage from the browser.js file in the same directory
336 const { launchChrome } = require("./browser");
337
338 // Flow 1 => Launching chrome and opening a new tab/page
339 const [newPage, exitChrome] = await launchChrome();
340 const [page] = await newPage();
341
342 const emailSelector="input[name=email]";
343 const pwdSelector="input[name=pwd]";
344 const btnSelector=".btn";
345
346 // exit the function if the tab is not properly opened
347 if (!page) return;
348
349 // Flow 2 => Visiting a website's home page
350 const url = "https://select.live/";
351 console.log("Opening " + url);
352 try {
353 await page.goto(url, {
354 waitUntil: "networkidle0", // wait till all network requests has been processed
355 });
356 } catch(e) {
357 console.error("Unable to visit " + url, e);
358 await exitChrome(); // close chrome on error
359 return; // exiting the function
360 }
361
362 //Perform the Login
363 await page.waitForSelector(emailSelector);
364 console.log('40 Found name="email" on page');
365
366 await page.waitForSelector(pwdSelector);
367 console.log('50 Found name="pwd" on page');
368
369 await page.waitForSelector(btnSelector);
370 console.log('55 Found the button with class namne btn');
371
372 await page.type(emailSelector, 'Username Goes Here');
373 await page.type(pwdSelector, 'Password Goes Here');
374 console.log('60 Entered email and password');
375
376 //Click the Login Butotn
377 try{
378 await page.click(btnSelector);
379 console.log('70 Clicked the Login Button');
380 }
381 catch(e){
382 console.error('Unable to click the login button' + btnSelector + ' ', e)
383 }
384
385 // Find the Power Percentage Value
386
387
388 await exitChrome(); // close chrome
389 console.log('900 Exited Chrome')
390};
391
392module.exports = scrapePersons;// systems table row
393const socSelector = '#ownerSystems tbody tr';
394
395// wait for it to render
396const socCell = await page.waitForSelector(socSelector, {
397 visible: true
398});
399
400// extract value from third cell for each row
401const socVal = await page.evaluate(socSelector => Array.from(document.querySelectorAll(`${socSelector} td:nth-child(3)`)).map(el => el.textContent), socSelector);
402
403console.log(socVal);
404
QUESTION
Android Studio BumbleBee pair wifi not working
Asked 2022-Apr-03 at 10:29I am trying to connect my Android 11 device with android studio over adb wifi but it is not working.
I updated to latest stable bumblebee and updated my SDK I tried turning off firewall on my pc but it is same result.
When I use QR code method my android phone just shows "pairing device" and nothing happens If I try the code method, android studio just shows "searching for devices" but nothing happens
and, yes, I enabled wireless debugging on my phone and I am connected to the same wifi network.
I don't know if the problem is with my computer or phone. I do not have any other Android11+ phone to try with
ANSWER
Answered 2022-Jan-30 at 21:44I was having the same problem as you. Neither pairing by QR nor by pairing code worked.
So I tried connecting by typing adb connect [phone_ip]:[port]
in the terminal and that worked flawlessly. Didn't even need to plug the phone to the computer with USB. Your phone will tell you the IP and port right above the "pair with QR code" option inside the Wifi-debugging setting. Just connect to that address.
QUESTION
Android Studio Bumblebee Wifi pairing Issue
Asked 2022-Mar-29 at 15:31I have used Android Studio Bumblebee's latest function (Wifi pairing) for 2 - 3 days before it stopped working.
I am now receiving the error "This system does not meet the requirements to support Wi-Fi pairing. Please update to the latest version of "platform-tools" using the SDK manager"
I have updated everything to the latest version.
ANSWER
Answered 2022-Feb-02 at 03:53My guess is that you have an old version of platform-tools/adb installed somewhere (you can verify this by running which adb
in your command prompt).
You can find the pathway to the platform-tools/adb you want to use in Android Studios under Settings -> Appearance & Behavior -> System Settings -> Android SDK.
Inside of this folder should be another folder called "platform-tools".
Update your PATH You'll want to add this folder to your PATH and remove the old one. Restart Android Studio For the changes to take effect, you'll need to restart the IDE.File -> Invalidate Caches -> Invalidate and Restart
Another Solution If the above doesn't work, you can also uninstall and reinstall platform-tools using the sdkmanager command.1sdkmanager --uninstall "platform-tools"
2
Once platform-tools is uninstalled, you can use the SDK Manager in Android Studio to reinstall.
QUESTION
Is the Android Wifi-API really so broken on Android 10+?
Asked 2022-Mar-22 at 11:19I'm working on a Wifi auto connect feature and I am shocked how broken that API is.
I'm using now 5 different APIs and I still don't get it in a way the user would expect it.
I have a setting to enable wifi auto connection on Android 10+ I'll try this:
- Check if I hold the
ACCESS_WIFI_STATE
permission with:
1appOpsManager.unsafeCheckOp("android:change_wifi_state", Process.myUid(), context.packageName) == AppOpsManager.MODE_ALLOWED
2
wifiManager.networkSuggestions.isNotEmpty()
if that is true I check which Wifi I'm currently connected with see step x. On lower levels I skip this and go to step 3WifiNetworkSuggestion.Builder()
and suggest the user my wifi with
1appOpsManager.unsafeCheckOp("android:change_wifi_state", Process.myUid(), context.packageName) == AppOpsManager.MODE_ALLOWED
2val status = wifiManager.addNetworkSuggestions(listOf(suggestion))
3// Strage bug: On first usage the return value is always SUCCESS
4val success = status == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS
5
Settings.ACTION_WIFI_ADD_NETWORKS
intent which can always add Wifis (with the downside that the user can share the wifi password)ACCESS_WIFI_STATE
, ACCESS_NETWORK_STATE
, ACCESS_COARSE_LOCATION
and ACCESS_FINE_LOCATION
. However the ConnectivityManager
API don't return the actual SSID
, that drives me crazy.Settings.Panel.ACTION_WIFI
action to let the user to connect to the right wifiTo summarize that:
- I use the
WifiNetworkSuggestion
API - I fallback to
Settings.ACTION_WIFI_ADD_NETWORKS
where possible - I try to verify if the user is currently connected to the right wifi
- I try to assist the user to conenct to the right wifi (or even turn wifi on) with the
Settings.Panel.ACTION_WIFI
action
Is that really such a mess or is there a easier way?
I'm currently accessing the SSID like this:
1appOpsManager.unsafeCheckOp("android:change_wifi_state", Process.myUid(), context.packageName) == AppOpsManager.MODE_ALLOWED
2val status = wifiManager.addNetworkSuggestions(listOf(suggestion))
3// Strage bug: On first usage the return value is always SUCCESS
4val success = status == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS
5private val currentSsid: String?
6 get() =
7 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
8 val wifiInfo = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)?.transportInfo as? WifiInfo
9 wifiInfo?.ssid
10 } else {
11 wifiManager.connectionInfo?.ssid
12 }
13
Based on Unable to get WIFI SSID using onCapabilitiesChanged in Android 12 I think that way I access the value is not supported. I get currently the value "<unknown ssid>"
.
ANSWER
Answered 2022-Mar-22 at 11:19Well just a half answer, but it might help anyway. Here is how I get the current SSID of the user (you need to hold the location permission):
1appOpsManager.unsafeCheckOp("android:change_wifi_state", Process.myUid(), context.packageName) == AppOpsManager.MODE_ALLOWED
2val status = wifiManager.addNetworkSuggestions(listOf(suggestion))
3// Strage bug: On first usage the return value is always SUCCESS
4val success = status == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS
5private val currentSsid: String?
6 get() =
7 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
8 val wifiInfo = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)?.transportInfo as? WifiInfo
9 wifiInfo?.ssid
10 } else {
11 wifiManager.connectionInfo?.ssid
12 }
13fun updateSsid(networkCapabilities: NetworkCapabilities) {
14 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
15 currentSsid = (networkCapabilities.transportInfo as? WifiInfo)?.ssid?.trim('"')
16 }
17}
18
19wifiCallback = when {
20 Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
21 object: ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
22 override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
23 updateSsid(networkCapabilities)
24 }
25 }
26 }
27 Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> {
28 object: ConnectivityManager.NetworkCallback() {
29 override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
30 updateSsid(networkCapabilities)
31 }
32 }
33 }
34 else -> null
35}
36
37wifiCallback?.let {
38 fragment.lifecycle.addObserver(object: DefaultLifecycleObserver {
39 override fun onResume(owner: LifecycleOwner) {
40 connectivityManager.registerNetworkCallback(request, it)
41 }
42
43 override fun onPause(owner: LifecycleOwner) {
44 connectivityManager.unregisterNetworkCallback(it)
45 }
46 })
47}
48
So basically aspects of your code need to check apis for the levels <10, 10, 11 and 12. What a mess.
This might be quiet handy if you want the user to pick the configured wifi:
1appOpsManager.unsafeCheckOp("android:change_wifi_state", Process.myUid(), context.packageName) == AppOpsManager.MODE_ALLOWED
2val status = wifiManager.addNetworkSuggestions(listOf(suggestion))
3// Strage bug: On first usage the return value is always SUCCESS
4val success = status == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS
5private val currentSsid: String?
6 get() =
7 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
8 val wifiInfo = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)?.transportInfo as? WifiInfo
9 wifiInfo?.ssid
10 } else {
11 wifiManager.connectionInfo?.ssid
12 }
13fun updateSsid(networkCapabilities: NetworkCapabilities) {
14 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
15 currentSsid = (networkCapabilities.transportInfo as? WifiInfo)?.ssid?.trim('"')
16 }
17}
18
19wifiCallback = when {
20 Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
21 object: ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
22 override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
23 updateSsid(networkCapabilities)
24 }
25 }
26 }
27 Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> {
28 object: ConnectivityManager.NetworkCallback() {
29 override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
30 updateSsid(networkCapabilities)
31 }
32 }
33 }
34 else -> null
35}
36
37wifiCallback?.let {
38 fragment.lifecycle.addObserver(object: DefaultLifecycleObserver {
39 override fun onResume(owner: LifecycleOwner) {
40 connectivityManager.registerNetworkCallback(request, it)
41 }
42
43 override fun onPause(owner: LifecycleOwner) {
44 connectivityManager.unregisterNetworkCallback(it)
45 }
46 })
47}
48fun showWifiDialog() {
49 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
50 try {
51 fragment.startActivity(Intent(android.provider.Settings.Panel.ACTION_WIFI))
52 } catch (e: Exception) {
53 // ignored
54 }
55 }
56}
57
QUESTION
Should macOS daemons be made from the "Command Line Tool" Xcode template?
Asked 2022-Mar-20 at 19:40I have a few questions regarding daemons. Indeed, even the macos developer center has limited information resources.
I want to develop an application daemon that runs after system boot without login.
a) a Daemon; Is it a simple console application combined with a plist? Because there are almost no tutorials on daemon development related to Xcode. If there is a code sample reference, can you share it here?
b) Can daemons be downloaded from the app store? Because there must be a software that I can offer to everyone through the App Store. Is the installation automatic like other app store apps? If anyone has experience and can share it, I would be very grateful.
c) I am working on an API related to mirroring the screen to Android phone. Do you think a daemon has full access to WiFi/BLE and screen capture APIs?
I would be very happy to hear your suggestions.
ANSWER
Answered 2022-Mar-20 at 19:32I've made a launch daemon in the past, for the purpose of making a privileged helper tool with SMBless
. I can share some of my experience.
a Daemon; Is it a simple console application combined with a plist? Because there are almost no tutorials on daemon development related to xcode. If there is a code sample reference, can you share it here?
Here are some resources that I found useful:
- Woody's Cocoa: implement a privileged Helper. This article covers the low-level, step by step process of making a launch daemon and launching it as a privileged helper tool. If you have no need for privileged execution, the steps would be much the same, but without the
SMJobBless
parts. - SwiftAuthorizationSample which show cases SecureXPC (a framework for Swifty,
Codable
-based XPC communication) and Blessed (a framework for a Swifty, modern interface to SMJobBless and the AuthorizationServices framework). It handles a lot of the complexity from #1. - Apple's Daemons and Services Programming Guide
Can daemons be downloaded from the app store? Because there must be a software that I can offer to everyone through the app store. Is the installation automatic like other app store apps? If anyone has experience and can share it, I would be very grateful.
No. You would distribute them as part of an app, and make your app install them when required.
I am working on an api related to mirroring the screen to android phone. Do you think a daemon has full access to WiFi/BLE and screen capture APIs?
WiFi certainly, but I'm not sure about the screen capture APIs. One of the differences between launch agents and daemons (IIRC), is that only launch agents can connect to the window server, which I assume is necessary for the screen capture APIs.
From Technical Note TN2083 – Daemons and Agents:
DaemonsA daemon is a program that runs in the background as part of the overall system (that is, it is not tied to a particular user). A daemon cannot display any GUI; more specifically, it is not allowed to connect to the window server. A web server is the perfect example of a daemon.
...
AgentsAn agent is a process that runs in the background on behalf of a particular user. Agents are useful because they can do things that daemons can't, like reliably access the user's home directory or connect to the window server. A calendar monitoring program is a good example of an agent because:
QUESTION
How to rebuild epoll package in electron?
Asked 2022-Mar-18 at 11:41I try to rebuild an electron app but I got this error regarding the epoll installation.
1Building module: epoll, Completed: 0gyp: name 'openssl_fips' is not defined while evaluating condition 'openssl_fips != ""' in binding.gyp while trying to load binding.gyp
2✖ Rebuild Failed
3An unhandled error occurred inside electron-rebuild
4node-gyp failed to rebuild '/home/pi/ma-0042-cihaz-be/node_modules/epoll'.
5For more information, rerun with the DEBUG environment variable set to "electron-rebuild".
6Error: `gyp` failed with exit code: 1
7Error: node-gyp failed to rebuild '/home/pi/ma-0042-cihaz-be/node_modules/epoll'.
8For more information, rerun with the DEBUG environment variable set to "electron-rebuild".
9at NodeGyp.rebuildModule (/home/pi/ma-0042-cihaz-be/node_modules/electron-rebuild/lib/src/module-type/node-gyp.js:109:19)
10 at processTicksAndRejections (internal/process/task_queues.js:95:5)
11 at async ModuleRebuilder.rebuildNodeGypModule (/home/pi/ma-0042-cihaz-be/node_modules/electron-rebuild/lib/src/module-rebuilder.js:94:9)
12 at async Rebuilder.rebuildModuleAt (/home/pi/ma-0042-cihaz-be/node_modules/electron-rebuild/lib/src/rebuild.js:226:9)
13 at async Rebuilder.rebuild (/home/pi/ma-0042-cihaz-be/node_modules/electron-rebuild/lib/src/rebuild.js:184:17)
14 at async /home/pi/ma-0042-cihaz-be/node_modules/electron-rebuild/lib/src/cli.js:154:9
15
16
I am using a raspberry, I did update it too. But it didn't work. These are the dependencies installed.
1Building module: epoll, Completed: 0gyp: name 'openssl_fips' is not defined while evaluating condition 'openssl_fips != ""' in binding.gyp while trying to load binding.gyp
2✖ Rebuild Failed
3An unhandled error occurred inside electron-rebuild
4node-gyp failed to rebuild '/home/pi/ma-0042-cihaz-be/node_modules/epoll'.
5For more information, rerun with the DEBUG environment variable set to "electron-rebuild".
6Error: `gyp` failed with exit code: 1
7Error: node-gyp failed to rebuild '/home/pi/ma-0042-cihaz-be/node_modules/epoll'.
8For more information, rerun with the DEBUG environment variable set to "electron-rebuild".
9at NodeGyp.rebuildModule (/home/pi/ma-0042-cihaz-be/node_modules/electron-rebuild/lib/src/module-type/node-gyp.js:109:19)
10 at processTicksAndRejections (internal/process/task_queues.js:95:5)
11 at async ModuleRebuilder.rebuildNodeGypModule (/home/pi/ma-0042-cihaz-be/node_modules/electron-rebuild/lib/src/module-rebuilder.js:94:9)
12 at async Rebuilder.rebuildModuleAt (/home/pi/ma-0042-cihaz-be/node_modules/electron-rebuild/lib/src/rebuild.js:226:9)
13 at async Rebuilder.rebuild (/home/pi/ma-0042-cihaz-be/node_modules/electron-rebuild/lib/src/rebuild.js:184:17)
14 at async /home/pi/ma-0042-cihaz-be/node_modules/electron-rebuild/lib/src/cli.js:154:9
15
16{
17
18 "dependencies": {
19 "@babel/preset-react": "^7.14.5",
20 "bcryptjs": "^2.4.3",
21 "body-parser": "^1.19.0",
22 "cors": "^2.8.5",
23 "dotenv": "^10.0.0",
24 "express": "^4.17.1",
25 "history": "^5.1.0",
26 "i2c-bus": "^5.2.2",
27 "jest": "^27.3.1",
28 "jest-fetch-mock": "^3.0.3",
29 "jsonwebtoken": "^8.5.1",
30 "mfrc522-rpi": "^2.1.3",
31 "moment": "^2.29.1",
32 "network-config": "^3.0.0",
33 "node-cron": "^3.0.0",
34 "node-fetch": "^2.6.5",
35 "node-wifi": "^2.0.15",
36 "pi-camera": "^1.6.0",
37 "react": "^17.0.2",
38 "react-dom": "^17.0.2",
39 "react-router-dom": "^6.0.0",
40 "realm": "^10.7.0",
41 "rpi-gpio": "^2.1.7",
42 "rpi-softspi": "^1.0.5",
43 "rpio": "^2.4.2",
44 "source-map-support": "^0.5.16"
45 },
46 "devDependencies": {
47 "@babel/core": "^7.15.5",
48 "@babel/node": "^7.15.4",
49 "@babel/plugin-transform-runtime": "^7.16.0",
50 "@babel/preset-env": "^7.15.6",
51 "babel-loader": "^8.2.2",
52 "electron": "8.2.0",
53 "electron-builder": "^22.11.7",
54 "electron-rebuild": "^3.2.3",
55 "electron-webpack": "^2.8.2",
56 "enzyme": "^3.11.0",
57 "enzyme-adapter-react-16": "^1.15.6",
58 "enzyme-to-json": "^3.6.2",
59 "webpack": "~4.42.1"
60 }
61}
62
I deleted node_modules with package-lock.json and installed everything again. I updated the raspberry with apt-get upgrade. I don't know how can I solve this issue. Do you have any ideas?
ANSWER
Answered 2021-Nov-09 at 06:01I have a same problem too, but i am using a serialport not epoll.
So, I think the cause of this problem is electron modules not the native module.
1Building module: epoll, Completed: 0gyp: name 'openssl_fips' is not defined while evaluating condition 'openssl_fips != ""' in binding.gyp while trying to load binding.gyp
2✖ Rebuild Failed
3An unhandled error occurred inside electron-rebuild
4node-gyp failed to rebuild '/home/pi/ma-0042-cihaz-be/node_modules/epoll'.
5For more information, rerun with the DEBUG environment variable set to "electron-rebuild".
6Error: `gyp` failed with exit code: 1
7Error: node-gyp failed to rebuild '/home/pi/ma-0042-cihaz-be/node_modules/epoll'.
8For more information, rerun with the DEBUG environment variable set to "electron-rebuild".
9at NodeGyp.rebuildModule (/home/pi/ma-0042-cihaz-be/node_modules/electron-rebuild/lib/src/module-type/node-gyp.js:109:19)
10 at processTicksAndRejections (internal/process/task_queues.js:95:5)
11 at async ModuleRebuilder.rebuildNodeGypModule (/home/pi/ma-0042-cihaz-be/node_modules/electron-rebuild/lib/src/module-rebuilder.js:94:9)
12 at async Rebuilder.rebuildModuleAt (/home/pi/ma-0042-cihaz-be/node_modules/electron-rebuild/lib/src/rebuild.js:226:9)
13 at async Rebuilder.rebuild (/home/pi/ma-0042-cihaz-be/node_modules/electron-rebuild/lib/src/rebuild.js:184:17)
14 at async /home/pi/ma-0042-cihaz-be/node_modules/electron-rebuild/lib/src/cli.js:154:9
15
16{
17
18 "dependencies": {
19 "@babel/preset-react": "^7.14.5",
20 "bcryptjs": "^2.4.3",
21 "body-parser": "^1.19.0",
22 "cors": "^2.8.5",
23 "dotenv": "^10.0.0",
24 "express": "^4.17.1",
25 "history": "^5.1.0",
26 "i2c-bus": "^5.2.2",
27 "jest": "^27.3.1",
28 "jest-fetch-mock": "^3.0.3",
29 "jsonwebtoken": "^8.5.1",
30 "mfrc522-rpi": "^2.1.3",
31 "moment": "^2.29.1",
32 "network-config": "^3.0.0",
33 "node-cron": "^3.0.0",
34 "node-fetch": "^2.6.5",
35 "node-wifi": "^2.0.15",
36 "pi-camera": "^1.6.0",
37 "react": "^17.0.2",
38 "react-dom": "^17.0.2",
39 "react-router-dom": "^6.0.0",
40 "realm": "^10.7.0",
41 "rpi-gpio": "^2.1.7",
42 "rpi-softspi": "^1.0.5",
43 "rpio": "^2.4.2",
44 "source-map-support": "^0.5.16"
45 },
46 "devDependencies": {
47 "@babel/core": "^7.15.5",
48 "@babel/node": "^7.15.4",
49 "@babel/plugin-transform-runtime": "^7.16.0",
50 "@babel/preset-env": "^7.15.6",
51 "babel-loader": "^8.2.2",
52 "electron": "8.2.0",
53 "electron-builder": "^22.11.7",
54 "electron-rebuild": "^3.2.3",
55 "electron-webpack": "^2.8.2",
56 "enzyme": "^3.11.0",
57 "enzyme-adapter-react-16": "^1.15.6",
58 "enzyme-to-json": "^3.6.2",
59 "webpack": "~4.42.1"
60 }
61}
62yarn run v1.22.15
63$ electron-rebuild -f -w myapp
64\ Building module: bindings, Completed: 0gyp: name 'openssl_fips' is not defined while evaluating condition 'openssl_fips != ""' in binding.gyp while trying to load binding.gyp
65× Rebuild Failed
66
67An unhandled error occurred inside electron-rebuild
68node-gyp failed to rebuild 'C:\Users\node_modules\@serialport\bindings'.
69For more information, rerun with the DEBUG environment variable set to "electron-rebuild".
70
71Error: `gyp` failed with exit code: 1
72
73
74
75Error: node-gyp failed to rebuild 'C:\Users\node_modules\@serialport\bindings'.
76For more information, rerun with the DEBUG environment variable set to "electron-rebuild".
77
78Error: `gyp` failed with exit code: 1
79
80
81 at NodeGyp.rebuildModule (C:\Users\node_modules\electron-rebuild\lib\src\module-type\node-gyp.js:109:19)
82 at processTicksAndRejections (internal/process/task_queues.js:95:5)
83 at async ModuleRebuilder.rebuildNodeGypModule (C:\Users\node_modules\electron-rebuild\lib\src\module-rebuilder.js:94:9)
84 at async Rebuilder.rebuildModuleAt (C:\Users\node_modules\electron-rebuild\lib\src\rebuild.js:226:9)
85 at async Rebuilder.rebuild (C:\Users\node_modules\electron-rebuild\lib\src\rebuild.js:184:17)
86 at async C:\Users\node_modules\electron-rebuild\lib\src\cli.js:154:9
87error Command failed with exit code 4294967295.
88info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
This problem started yesterday. There were no problems in the last week.
I already installed the windows-build-tools, node-gyp, python 3.x.
I set the npm config & yarn config & python Env path, msvs_version too.
I reinstalled above packages many many times but not work electron-rebuild.
That is whole Error messages. There is no more Error messages.
What should we do?
QUESTION
How to get a response from the async write function in capacitor-community / bluetooth-le
Asked 2022-Mar-18 at 01:42I'm working with an ESP32 chip and am trying to create an Android app (using Ionic) which allows user to send wifi credentials to the ESP32 chip via BLE. I'd like to be able to update the status of the wifi sending process for the user in the UI (which I'm developing using Angular and then converting it to an Android webapp using Ionic). To do this, I'm also using the capacitor-community/bluetooth-le library.
Can anyone explain to me what this.queue does in the async write function (code shown below) does? I thought this function returns a response from a remote BLE device after writing to a GATT characteristic, but I get absolutely nothing at all for a response.
1async write(deviceId: string, service: string, characteristic: string, value: DataView): Promise<void> {
2 service = validateUUID(service);
3 characteristic = validateUUID(characteristic);
4 return this.queue(async () => {
5 if (!value?.buffer) {
6 throw new Error('Invalid data.');
7 }
8 let writeValue: DataView | string = value;
9 if (Capacitor.getPlatform() !== 'web') {
10 // on native we can only write strings
11 writeValue = dataViewToHexString(value);
12 }
13 await BluetoothLe.write({
14 deviceId,
15 service,
16 characteristic,
17 value: writeValue,
18 });
19 });
20 }
21
Here's how I'm using the write function shown above:
1async write(deviceId: string, service: string, characteristic: string, value: DataView): Promise<void> {
2 service = validateUUID(service);
3 characteristic = validateUUID(characteristic);
4 return this.queue(async () => {
5 if (!value?.buffer) {
6 throw new Error('Invalid data.');
7 }
8 let writeValue: DataView | string = value;
9 if (Capacitor.getPlatform() !== 'web') {
10 // on native we can only write strings
11 writeValue = dataViewToHexString(value);
12 }
13 await BluetoothLe.write({
14 deviceId,
15 service,
16 characteristic,
17 value: writeValue,
18 });
19 });
20 }
21this.bleConnectService.getBLE().then(resp => {
22 this.deviceID = resp});
23}
24
25BleClient.write(this.deviceID, '021a9004-0382-4aea-bff4-6b3f1c5adfb4', '021aff54-0382-4aea-bff4-6b3f1c5adfb4', bytesOfStuff).then(resp => {
26 const result2 = resp;
27}
28
I know for a fact that the write function works because my chip is getting the data I'm sending in the right format. I'm just not getting a response back on the client side from the write function. Also, I can confirm the chip is sending a response each time it does something with the data I send to it.
ANSWER
Answered 2022-Mar-18 at 01:42Here's how I'm using BleClient.write to transmit information to a BLE device (recipient):
1async write(deviceId: string, service: string, characteristic: string, value: DataView): Promise<void> {
2 service = validateUUID(service);
3 characteristic = validateUUID(characteristic);
4 return this.queue(async () => {
5 if (!value?.buffer) {
6 throw new Error('Invalid data.');
7 }
8 let writeValue: DataView | string = value;
9 if (Capacitor.getPlatform() !== 'web') {
10 // on native we can only write strings
11 writeValue = dataViewToHexString(value);
12 }
13 await BluetoothLe.write({
14 deviceId,
15 service,
16 characteristic,
17 value: writeValue,
18 });
19 });
20 }
21this.bleConnectService.getBLE().then(resp => {
22 this.deviceID = resp});
23}
24
25BleClient.write(this.deviceID, '021a9004-0382-4aea-bff4-6b3f1c5adfb4', '021aff54-0382-4aea-bff4-6b3f1c5adfb4', bytesOfStuff).then(resp => {
26 const result2 = resp;
27}
28BleClient.write(this.connectedDevice.deviceId, this.baseUUID, this.characteristicUUID, data)
29.then(console.log('BleWrite was successful. Now let/'s do something else')
30.catch(error => console.log('BleWrite fail error: ', error))
31
BleClient.write returns undefined (promise) when resolved. So when it resolves (ie, BleClient.write is successful), it doesn't fetch and return any confirming data from the recipient device it writes to. You could say the absence of an error implies BleClient.write was successful. If BleClient.write rejects, it will return an error.
But just because BleClient.write resolves doesn't mean that the recipient device received and recorded the information at its end. If there's a connection breakdown between the initiating and recipient devices, BleClient.write may very well have completed its share of the transmission and resolved, but the recipient device may still not have received the data. To make sure your recipient device gets the data as intended, there has to be some logic at the recipient device's end to process the incoming data and write something to its own GATT characteristic indicating successful receipt of data from the initiating device. The initiating device can then read this information from the recipient device's GATT characteristic using BleClient.read() to get confirmation that the recipient device did in fact get the data.
QUESTION
Android Studio Disconnects From Physical Device
Asked 2022-Mar-06 at 15:11Android Studio Bumblebee (2021.1.1) was released stably on 25 January 2022 bundled with a new Device Manager (accompanying new support for Android 11+ device debugging over WIFI). I jumped on this stable release, updating from Android Studio Arctic Fox (2020.3.1 Patch 4).
Unfortunately however, since updating, physical devices/handsets don't remain connected to Android Studio for the purpose of debugging. I can confirm that the issue was introduced from Android Studio Bumblebee onwards (occurring in Beta and Canary builds also). I've reproduced the issue on Android Studio Bumblebee (Stable), Chipmunk (Beta), and Dolphin (Canary), but Android Studio Arctic Fox (superseded Stable) continues to work just fine.
The issue occurs soon after opening Android Studio (Bumblebee+) with one of my physical devices connected. Everything appears fine initially and I may even have enough time to deploy my project to the handset, before the device disappears from Android Studio (as if I'd physically disconnected the USB cable from my computer or from the handset itself).
I've tried a fair few things in an attempt to determine a root cause. These include testing:
- With different USB cables.
- With different handsets (both varying makes and models).
- With various versions of the Android Studio IDE (as mentioned above).
- Plugging the USB cables into different USB ports on my computer.
- Rebooting handsets and my computer.
- Restarting Android Studio.
- Invalidating caches and restarting Android Studio.
adb kill-server
thenadb start-server
.- Revoking/reaccepting USB debugging authorization.
- Reinstalled build tools/platform tools, and ADB.
- A great number of further possibilities, to no avail.
I searched and read through remotely similar issues, including (but not limited to) these:
- Android Studio Arctic Fox (Adb) - Connected Devices are being disconnected after some time
- Android debugger continually disconnects
This particular comment in one of the above issues clued me onto a possible root cause:
I have been fighting for a few days with adb not seeing my device. After trying many other posted solutions, I discovered that the issue was with Chrome also trying to connect its debugger to a web view. If Chrome is connected using chrome://inspect, then adb seems to disconnect. Quitting Chrome resolves the issue. Then I can connect with Android Studio and then restart Chrome and reconnect. Hope this helps someone else.
However I've been unable to do anything with the above discovery, other than close Google Chrome, and hope for the best. Obviously this isn't an ideal solution. It appears as though the moment Google Chrome shows the connected physical device in the chrome://inspect/#devices page, the physical device promptly becomes unavailable through Android Studio.
I've jumped back to Android Studio Arctic Fox (2020.3.1 Patch 4) for the moment, however this brings with it other issues (my current core project targets the latest SDK version, which requires the updated IDE).
Absolutely any help with this would be insanely appreciated. I've exhausted just about every avenue that I can think of!
ANSWER
Answered 2022-Feb-01 at 17:29I solved the problem by disabling
Settings -> Build, Execution, Deployment -> Debugger -> "Enable adb mDNS for wireless debugging"
QUESTION
Winsock sendto returns error 10049 (WSAEADDRNOTAVAIL) for broadcast address after network adapter is disabled or physically disconnected
Asked 2022-Mar-01 at 16:10I am working on a p2p application and to make testing simple, I am currently using udp broadcast for the peer discovery in my local network. Each peer binds one udp socket to port 29292 of the ip address of each local network interface (discovered via GetAdaptersInfo
) and each socket periodically sends a packet to the broadcast address of its network interface/local address. The sockets are set to allow port reuse (via setsockopt
SO_REUSEADDR
), which enables me to run multiple peers on the same local machine without any conflicts. In this case there is only a single peer on the entire network though.
This all works perfectly fine (tested with 2 peers on 1 machine and 2 peers on 2 machines) UNTIL a network interface is disconnected. When deactivacting the network adapter of either my wifi or an USB-to-LAN adapter in the windows dialog, or just plugging the usb cable of the adapter, the next call to sendto
will fail with return code 10049
. It doesn't matter if the other adapter is still connected, or was at the beginning, it will fail. The only thing that doesn't make it fail is deactivating wifi through the fancy win10 dialog through the taskbar, but that isn't really a surprise because that doesn't deactivate or remove the adapter itself.
I initially thought that this makes sense because when the nic is gone, how should the system route the packet. But: The fact that the packet can't reach its target has absolutely nothing to do with the address itsself being invalid (which is what the error means), so I suspect I am missing something here. I was looking for any information I could use to detect this case and distinguish it from simply trying to sendto
INADDR_ANY
, but I couldn't find anything. I started to log every bit of information which I suspected could have changed, but its all the same on a successfull sendto
and the one that crashes (retrieved via getsockopt
):
1250 16.24746[886] [debug|debug] local address: 192.168.178.35
2251 16.24812[886] [debug|debug] no remote address
3252 16.25333[886] [debug|debug] type: SOCK_DGRAM
4253 16.25457[886] [debug|debug] protocol: IPPROTO_UDP
5254 16.25673[886] [debug|debug] broadcast: 1, dontroute: 0, max_msg_size: 65507, rcv_buffer: 65536, rcv_timeout: 0, reuse_addr: 1, snd_buffer: 65536, sdn_timeout: 0
6255 16.25806[886] [debug|debug] Last WSA error on socket was WSA Error Code 0: The operation completed successfully.
7
8256 16.25916[886] [debug|debug] target address windows formatted: 192.168.178.255
9257 16.25976[886] [debug|debug] target address 192.168.178.255:29292
10258 16.26138[886] [debug|assert] ASSERT FAILED at D:\Workspaces\spaced\source\platform\win32_platform.cpp:4141: sendto failed with (unhandled) WSA Error Code 10049: The requested address is not valid in its context.
11
The nic that got removed is this one:
1250 16.24746[886] [debug|debug] local address: 192.168.178.35
2251 16.24812[886] [debug|debug] no remote address
3252 16.25333[886] [debug|debug] type: SOCK_DGRAM
4253 16.25457[886] [debug|debug] protocol: IPPROTO_UDP
5254 16.25673[886] [debug|debug] broadcast: 1, dontroute: 0, max_msg_size: 65507, rcv_buffer: 65536, rcv_timeout: 0, reuse_addr: 1, snd_buffer: 65536, sdn_timeout: 0
6255 16.25806[886] [debug|debug] Last WSA error on socket was WSA Error Code 0: The operation completed successfully.
7
8256 16.25916[886] [debug|debug] target address windows formatted: 192.168.178.255
9257 16.25976[886] [debug|debug] target address 192.168.178.255:29292
10258 16.26138[886] [debug|assert] ASSERT FAILED at D:\Workspaces\spaced\source\platform\win32_platform.cpp:4141: sendto failed with (unhandled) WSA Error Code 10049: The requested address is not valid in its context.
11 1.07254[0] [platform|info] Discovered Network Interface "Realtek USB GbE Family Controller" with IP 192.168.178.35 and Subnet 255.255.255.0
12
And this is the code that does the sending (dlog_socket_information_and_last_wsaerror
generates all the output that is gathered using getsockopt
):
1250 16.24746[886] [debug|debug] local address: 192.168.178.35
2251 16.24812[886] [debug|debug] no remote address
3252 16.25333[886] [debug|debug] type: SOCK_DGRAM
4253 16.25457[886] [debug|debug] protocol: IPPROTO_UDP
5254 16.25673[886] [debug|debug] broadcast: 1, dontroute: 0, max_msg_size: 65507, rcv_buffer: 65536, rcv_timeout: 0, reuse_addr: 1, snd_buffer: 65536, sdn_timeout: 0
6255 16.25806[886] [debug|debug] Last WSA error on socket was WSA Error Code 0: The operation completed successfully.
7
8256 16.25916[886] [debug|debug] target address windows formatted: 192.168.178.255
9257 16.25976[886] [debug|debug] target address 192.168.178.255:29292
10258 16.26138[886] [debug|assert] ASSERT FAILED at D:\Workspaces\spaced\source\platform\win32_platform.cpp:4141: sendto failed with (unhandled) WSA Error Code 10049: The requested address is not valid in its context.
11 1.07254[0] [platform|info] Discovered Network Interface "Realtek USB GbE Family Controller" with IP 192.168.178.35 and Subnet 255.255.255.0
12void send_slice_over_udp_socket(Socket_Handle handle, Slice<d_byte> buffer, u32 remote_ip, u16 remote_port){
13 PROFILE_FUNCTION();
14
15 auto socket = (UDP_Socket*) sockets[handle.handle];
16 ASSERT_VALID_UDP_SOCKET(socket);
17 dlog_socket_information_and_last_wsaerror(socket);
18
19 if(socket->is_dummy)
20 return;
21
22 if(buffer.size == 0)
23 return;
24
25 DASSERT(socket->state == Socket_State::created);
26
27 u64 bytes_left = buffer.size;
28
29 sockaddr_in target_socket_address = create_socket_address(remote_ip, remote_port);
30
31 #pragma warning(push)
32 #pragma warning(disable: 4996)
33 dlog("target address windows formatted: %s", inet_ntoa(target_socket_address.sin_addr));
34 #pragma warning(pop)
35 unsigned char* parts = (unsigned char*)&remote_ip;
36 dlog("target address %hhu.%hhu.%hhu.%hhu:%hu", parts[3], parts[2], parts[1], parts[0], remote_port);
37
38 int sent_bytes = sendto(socket->handle, (char*) buffer.data, bytes_left > (u64) INT32_MAX ? INT32_MAX : (int) bytes_left, 0, (sockaddr*)&target_socket_address, sizeof(target_socket_address));
39
40 if(sent_bytes == SOCKET_ERROR){
41 #define LOG_WARNING(message) log_nonreproducible(message, Category::platform_network, Severity::warning, socket->handle); return;
42 switch(WSAGetLastError()){
43 //@TODO handle all (more? I guess many should just be asserted since they should never happen) cases
44 case WSAEHOSTUNREACH: LOG_WARNING("socket %lld, send failed: The remote host can't be reached at this time.");
45 case WSAECONNRESET: LOG_WARNING("socket %lld, send failed: Multiple UDP packet deliveries failed. According to documentation we should close the socket. Not sure if this makes sense, this is a UDP port after all. Closing the socket wont change anything, right?");
46 case WSAENETUNREACH: LOG_WARNING("socket %lld, send failed: the network cannot be reached from this host at this time.");
47 case WSAETIMEDOUT: LOG_WARNING("socket %lld, send failed: The connection has been dropped, because of a network failure or because the system on the other end went down without notice.");
48
49 case WSAEADDRNOTAVAIL:
50
51 case WSAENETRESET:
52 case WSAEACCES:
53 case WSAEWOULDBLOCK: //can this even happen on a udp port? I expect this to be fire-and-forget-style.
54 case WSAEMSGSIZE:
55 case WSANOTINITIALISED:
56 case WSAENETDOWN:
57 case WSAEINVAL:
58 case WSAEINTR:
59 case WSAEINPROGRESS:
60 case WSAEFAULT:
61 case WSAENOBUFS:
62 case WSAENOTCONN:
63 case WSAENOTSOCK:
64 case WSAEOPNOTSUPP:
65 case WSAESHUTDOWN:
66 case WSAECONNABORTED:
67 case WSAEAFNOSUPPORT:
68 case WSAEDESTADDRREQ:
69 ASSERT(false, tprint_last_wsa_error_as_formatted_message("sendto failed with (unhandled) ")); break;
70 default: ASSERT(false, tprint_last_wsa_error_as_formatted_message("sendto failed with (undocumented) ")); //The switch case above should have been exhaustive. This is a bug. We either forgot a case, or maybe the docs were lying? (That happened to me on android. Fun times. Well. Not really.)
71 }
72 #undef LOG_WARNING
73 }
74
75 DASSERT(sent_bytes >= 0);
76 total_bytes_sent += (u64) sent_bytes;
77 bytes_left -= (u64) sent_bytes;
78 DASSERT(bytes_left == 0);
79}
80
The code that generates the address from ip and port looks like this:
1250 16.24746[886] [debug|debug] local address: 192.168.178.35
2251 16.24812[886] [debug|debug] no remote address
3252 16.25333[886] [debug|debug] type: SOCK_DGRAM
4253 16.25457[886] [debug|debug] protocol: IPPROTO_UDP
5254 16.25673[886] [debug|debug] broadcast: 1, dontroute: 0, max_msg_size: 65507, rcv_buffer: 65536, rcv_timeout: 0, reuse_addr: 1, snd_buffer: 65536, sdn_timeout: 0
6255 16.25806[886] [debug|debug] Last WSA error on socket was WSA Error Code 0: The operation completed successfully.
7
8256 16.25916[886] [debug|debug] target address windows formatted: 192.168.178.255
9257 16.25976[886] [debug|debug] target address 192.168.178.255:29292
10258 16.26138[886] [debug|assert] ASSERT FAILED at D:\Workspaces\spaced\source\platform\win32_platform.cpp:4141: sendto failed with (unhandled) WSA Error Code 10049: The requested address is not valid in its context.
11 1.07254[0] [platform|info] Discovered Network Interface "Realtek USB GbE Family Controller" with IP 192.168.178.35 and Subnet 255.255.255.0
12void send_slice_over_udp_socket(Socket_Handle handle, Slice<d_byte> buffer, u32 remote_ip, u16 remote_port){
13 PROFILE_FUNCTION();
14
15 auto socket = (UDP_Socket*) sockets[handle.handle];
16 ASSERT_VALID_UDP_SOCKET(socket);
17 dlog_socket_information_and_last_wsaerror(socket);
18
19 if(socket->is_dummy)
20 return;
21
22 if(buffer.size == 0)
23 return;
24
25 DASSERT(socket->state == Socket_State::created);
26
27 u64 bytes_left = buffer.size;
28
29 sockaddr_in target_socket_address = create_socket_address(remote_ip, remote_port);
30
31 #pragma warning(push)
32 #pragma warning(disable: 4996)
33 dlog("target address windows formatted: %s", inet_ntoa(target_socket_address.sin_addr));
34 #pragma warning(pop)
35 unsigned char* parts = (unsigned char*)&remote_ip;
36 dlog("target address %hhu.%hhu.%hhu.%hhu:%hu", parts[3], parts[2], parts[1], parts[0], remote_port);
37
38 int sent_bytes = sendto(socket->handle, (char*) buffer.data, bytes_left > (u64) INT32_MAX ? INT32_MAX : (int) bytes_left, 0, (sockaddr*)&target_socket_address, sizeof(target_socket_address));
39
40 if(sent_bytes == SOCKET_ERROR){
41 #define LOG_WARNING(message) log_nonreproducible(message, Category::platform_network, Severity::warning, socket->handle); return;
42 switch(WSAGetLastError()){
43 //@TODO handle all (more? I guess many should just be asserted since they should never happen) cases
44 case WSAEHOSTUNREACH: LOG_WARNING("socket %lld, send failed: The remote host can't be reached at this time.");
45 case WSAECONNRESET: LOG_WARNING("socket %lld, send failed: Multiple UDP packet deliveries failed. According to documentation we should close the socket. Not sure if this makes sense, this is a UDP port after all. Closing the socket wont change anything, right?");
46 case WSAENETUNREACH: LOG_WARNING("socket %lld, send failed: the network cannot be reached from this host at this time.");
47 case WSAETIMEDOUT: LOG_WARNING("socket %lld, send failed: The connection has been dropped, because of a network failure or because the system on the other end went down without notice.");
48
49 case WSAEADDRNOTAVAIL:
50
51 case WSAENETRESET:
52 case WSAEACCES:
53 case WSAEWOULDBLOCK: //can this even happen on a udp port? I expect this to be fire-and-forget-style.
54 case WSAEMSGSIZE:
55 case WSANOTINITIALISED:
56 case WSAENETDOWN:
57 case WSAEINVAL:
58 case WSAEINTR:
59 case WSAEINPROGRESS:
60 case WSAEFAULT:
61 case WSAENOBUFS:
62 case WSAENOTCONN:
63 case WSAENOTSOCK:
64 case WSAEOPNOTSUPP:
65 case WSAESHUTDOWN:
66 case WSAECONNABORTED:
67 case WSAEAFNOSUPPORT:
68 case WSAEDESTADDRREQ:
69 ASSERT(false, tprint_last_wsa_error_as_formatted_message("sendto failed with (unhandled) ")); break;
70 default: ASSERT(false, tprint_last_wsa_error_as_formatted_message("sendto failed with (undocumented) ")); //The switch case above should have been exhaustive. This is a bug. We either forgot a case, or maybe the docs were lying? (That happened to me on android. Fun times. Well. Not really.)
71 }
72 #undef LOG_WARNING
73 }
74
75 DASSERT(sent_bytes >= 0);
76 total_bytes_sent += (u64) sent_bytes;
77 bytes_left -= (u64) sent_bytes;
78 DASSERT(bytes_left == 0);
79}
80sockaddr_in create_socket_address(u32 ip, u16 port){
81 sockaddr_in address_info;
82 address_info.sin_family = AF_INET;
83 address_info.sin_port = htons(port);
84 address_info.sin_addr.s_addr = htonl(ip);
85 memset(address_info.sin_zero, 0, 8);
86 return address_info;
87}
88
The error seems to be a little flaky. It reproduces 100% of the time until it decides not to anymore. After a restart its usually back.
I am looking for a solution to handle this case correctly. I could of course just re-do the network interface discovery when the error occurs, because I "know" that I don't give any broken IPs to sendto, but that would just be a heuristic. I want to solve the actual problem.
I also don't quite understand when error 10049 is supposed to fire exactly anyway. Is it just if I pass an ipv6 address to a ipv4 socket, or send to 0.0.0.0? There is no flat out "illegal" ipv4 address after all, just ones that don't make sense from context.
If you know what I am missing here, please let me know!
ANSWER
Answered 2022-Mar-01 at 16:01This is a issue people have been facing up for a while , and people suggested to read the documentation provided by Microsoft on the following issue . "Btw , I don't know whether they are the same issues or not but the error thrown back the code are same, that's why I have attached a link for the same!!"
QUESTION
Compress & Upload large videos to Google cloud storage using Flutter/Dart
Asked 2022-Feb-20 at 03:41There are a couple of notable packages on pub.dev that offer video compression. I've tried them, and other sketchy packages, and none work well once a video gets around 300MB. They crash or have other issues on various platforms and hardware. Namely, video compress and light compressor. The GH commits and support are concerning as well on the packages I've seen for video compression in pub.dev. PR's not being pulled in and issues not being resolved in a timely manner and some quite serious for recent android APK updates. So not something I want in my dependency stack.
I am uploading to Google Cloud Storage using FlutterFire. While my code does upload using FireBaseStorage upload task it does not have any ability to compress on the client side or handle background uploading when the app is closed.
So, currently on the server side, I have a GCF that triggers on file uploaded. Then I use nodejs ffmpeg, which is baked into GCF's to compress server side and convert to H264. And finally delete the original large upload video and save the compressed video to storage.
This solution works, but depending on a user's connection and whether they are on wifi, can take an awful long time and when it fails or the user closes the app, my current solution is useless.
I wish there was a solid native library on Android and iOS, that I could tap into, to confidently perform compression and conversion from any format to H264 and also allow uploading, whether my app is closed or in the background, to GC storage. Any thoughts? I wish this was standard in FlutterFire's cloud storage handling!
I have yet to test flutter_ffmpeg, but only because some have said it runs so slowly on client. So again, Flutter/Dart can access natively written code, but I don't know where to start on Android/iOS to do this the right way. And I understand this is what some of the packages are doing, but they do not work with large videos, so I'm hoping someone can point me in the right direction on Android and iOS.
My code for handling upload tasks to GC storage.
1
2 Future<void> uploadTask({
3 required WidgetRef ref,
4 required File file,
5 required String objectPath,
6 SettableMetadata? metaData,
7 bool deleteAfterUpload = false,
8 bool displayProgressNotification = false,
9 }) async {
10 String filePath = file.path;
11 filename = basename(file.path);
12
13 /// Remove any instances of '//' from the path.
14 final String path = '$storagePath/$objectPath'.replaceAll('//', '/');
15 UploadTask task = storage.ref(path).putFile(
16 File(filePath),
17 metaData,
18 );
19
20 /// Store UploadTask in StateNotifierProvider to monitor progress.
21 ref.read(uploadingStateProvider.notifier).myUploadTask(task);
22 }
23
ANSWER
Answered 2022-Feb-20 at 03:41I did resolve, to some degree, my original post's questions and frustrations by using the ffmpeg_kit_flutter_full_gpl package on the client side, and then ffmpeg again in GCF on the server side. In summary:
- Within 60 seconds, I can now compress a 2 minute video by 90% before uploading to firebase storage.
- Using
onFinalize
via GCF on the server side I run ffmpeg again on the uploaded video and gain another 77% reduction in file size on the server side without any loss in video quality. - My solution does not yet upload while the app is closed.
- On the client side, this solution requires setting the camera
ResolutionPreset
tohigh
(720p), rather thanmax
, which can be a minimum of 1080p, and setting the ffmpeg-preset veryfast
rather than themedium
default.
Camera & ffmpeg solution settings:
- Flutter camera package
ResolutionPreset
tohigh
- ffmpeg
-preset veryfast
Transcoding results stats for 2 minute video:
- Before transcode: 255MB
- After client side transcode: 25MB (90% decrease in size before upload)
- Time to transcode: 60 seconds
onFinalized
GCF ffmpeg transcode: 19MB (77% reduction in size)- In total a 93% reduction in size while keep high quality 720p video.
flutter_ffmpeg is archived, the new ffmpeg flutter package is ffmpeg_kit_flutter.
That being said, I used ffmpeg_kit_flutter to build my solution on the client side, rather than the server side, and transcode the video before uploading.
Cons:
- Doubled my app size to use ffmpeg, because I needed access to both
lame
andx264
so I had to install the full-gpl package to gain access to these libraries. - A two minute video can take up to 60 seconds to transcode.
The pros:
- Low bandwidth connections will operate much better after a video is reduced in size by 90%.
- Large videos will transcode and ffmpegkit does not crash like other flutter packages I've tried.
- The second pass with ffmpeg on GCF gains another 77% reduction in size taking a video of 100's of MB's down to just 10-20 MB max for eventually delivery.
- Costs lower on the front and back end.
So, you'll have to decide if the pros outweighs the cons and if 720p is high enough quality for playback. For me 720p looks perfect for video playback on a mobile phone and 1080p or higher was big time overkill.
I've provided sample code (not full classes) to give anyone looking to implement my solution a try. It became very important, due to the amount of time to transcode, to display a progress meter so the user does not give up on the process. You'll see my simple solution to displaying transcoding progress.
pubspec.yaml
- camera package for video recording
- riverpod required for statenotifier and transcode/upload progress notifications
- ffmpeg_kit_flutter_full_gpl (the full_gpl gets the ffmpeg package with most libraries) required to get
h264
andlibmp3lame
encoders to produce most widely playable transcoded videos. - wakelock required because transcoding takes so long you don't want the phone to sleep while transcoding.
1
2 Future<void> uploadTask({
3 required WidgetRef ref,
4 required File file,
5 required String objectPath,
6 SettableMetadata? metaData,
7 bool deleteAfterUpload = false,
8 bool displayProgressNotification = false,
9 }) async {
10 String filePath = file.path;
11 filename = basename(file.path);
12
13 /// Remove any instances of '//' from the path.
14 final String path = '$storagePath/$objectPath'.replaceAll('//', '/');
15 UploadTask task = storage.ref(path).putFile(
16 File(filePath),
17 metaData,
18 );
19
20 /// Store UploadTask in StateNotifierProvider to monitor progress.
21 ref.read(uploadingStateProvider.notifier).myUploadTask(task);
22 }
23dependencies:
24 camera: ^0.9.4+12
25 flutter_riverpod: ^1.0.3
26 ffmpeg_kit_flutter_full_gpl: ^4.5.1
27 wakelock: ^0.5.6
28
Riverpod StateNotifier
classes to send progress updates to the ui for both transcoding and uploading to firebase storage. Assumption that users are familiar with Riverpod and StateNotifiers.
1
2 Future<void> uploadTask({
3 required WidgetRef ref,
4 required File file,
5 required String objectPath,
6 SettableMetadata? metaData,
7 bool deleteAfterUpload = false,
8 bool displayProgressNotification = false,
9 }) async {
10 String filePath = file.path;
11 filename = basename(file.path);
12
13 /// Remove any instances of '//' from the path.
14 final String path = '$storagePath/$objectPath'.replaceAll('//', '/');
15 UploadTask task = storage.ref(path).putFile(
16 File(filePath),
17 metaData,
18 );
19
20 /// Store UploadTask in StateNotifierProvider to monitor progress.
21 ref.read(uploadingStateProvider.notifier).myUploadTask(task);
22 }
23dependencies:
24 camera: ^0.9.4+12
25 flutter_riverpod: ^1.0.3
26 ffmpeg_kit_flutter_full_gpl: ^4.5.1
27 wakelock: ^0.5.6
28@immutable
29class TranscodeUploadMessage {
30 const TranscodeUploadMessage({
31 required this.id,
32 required this.statusTitle,
33 required this.statusMessage,
34 required this.uploadPercentage,
35 required this.isRunning,
36 required this.completed,
37 required this.showSpinner,
38 required this.showPercentage,
39 required this.showError,
40 });
41
42 final int id;
43 final String statusTitle;
44 final String statusMessage;
45 final String uploadPercentage;
46 final bool isRunning;
47 final bool completed;
48 final bool showSpinner;
49 final bool showPercentage;
50 final bool showError;
51
52 TranscodeUploadMessage copyWith({
53 int? id,
54 String? statusTitle,
55 String? statusMessage,
56 String? uploadPercentage,
57 bool? isRunning,
58 bool? completed,
59 bool? showSpinner,
60 bool? showPercentage,
61 bool? showError,
62 }) {
63 return TranscodeUploadMessage(
64 id: id ?? this.id,
65 statusTitle: statusTitle ?? this.statusTitle,
66 statusMessage: statusMessage ?? this.statusMessage,
67 uploadPercentage: uploadPercentage ?? this.uploadPercentage,
68 isRunning: isRunning ?? this.isRunning,
69 completed: completed ?? this.completed,
70 showSpinner: showSpinner ?? this.showSpinner,
71 showPercentage: showSpinner ?? this.showPercentage,
72 showError: showError ?? this.showError,
73 );
74 }
75}
76
77class TranscodeUploadMessageNotifier
78 extends StateNotifier<List<TranscodeUploadMessage>> {
79 TranscodeUploadMessageNotifier() : super([]);
80
81 /// Since our state is immutable, we are not allowed to do
82 /// `state.add(message)`. Instead, we should create a new list of messages which
83 /// contains the previous items and the new one.
84 ///
85 /// Using Dart's spread operator here is helpful!
86 void set(TranscodeUploadMessage message) {
87 state = [...state, message];
88 }
89
90 /// Our state is immutable. So we're making a new list instead of changing
91 /// the existing list.
92 void remove(int id) {
93 state = [
94 for (final message in state)
95 if (message.id != id) message,
96 ];
97 }
98
99 /// Update message. Since our state is immutable, we need to make a copy of
100 /// the message. We're using our `copyWith` method implemented before to help
101 /// with that.
102 void update(TranscodeUploadMessage messageUpdated) {
103 state = [
104 for (final message in state)
105 if (message.id == messageUpdated.id)
106
107 /// Use copyWith to update a message
108 message.copyWith(
109 statusTitle: messageUpdated.statusTitle,
110 statusMessage: messageUpdated.statusMessage,
111 uploadPercentage: messageUpdated.uploadPercentage,
112 isRunning: messageUpdated.isRunning,
113 completed: messageUpdated.completed,
114 showSpinner: messageUpdated.showSpinner,
115 showPercentage: messageUpdated.showPercentage,
116 showError: messageUpdated.showError,
117 )
118 else
119
120 /// other messages, which there are not any at this time, are not
121 /// modified
122 message,
123 ];
124 }
125}
126
127/// Using StateNotifierProvider to allow the UI to interact with our
128/// TranscodeUploadMessageNotifier class.
129final transcodeMessageProvider = StateNotifierProvider.autoDispose<
130 TranscodeUploadMessageNotifier, List<TranscodeUploadMessage>>((ref) {
131 return TranscodeUploadMessageNotifier();
132});
133
ffmpegkit package running ffmpeg commands and sending transcoding statistics to the StateNotifier. (Missing quite a bit of code, but pseudocode to demonstrate.)
1
2 Future<void> uploadTask({
3 required WidgetRef ref,
4 required File file,
5 required String objectPath,
6 SettableMetadata? metaData,
7 bool deleteAfterUpload = false,
8 bool displayProgressNotification = false,
9 }) async {
10 String filePath = file.path;
11 filename = basename(file.path);
12
13 /// Remove any instances of '//' from the path.
14 final String path = '$storagePath/$objectPath'.replaceAll('//', '/');
15 UploadTask task = storage.ref(path).putFile(
16 File(filePath),
17 metaData,
18 );
19
20 /// Store UploadTask in StateNotifierProvider to monitor progress.
21 ref.read(uploadingStateProvider.notifier).myUploadTask(task);
22 }
23dependencies:
24 camera: ^0.9.4+12
25 flutter_riverpod: ^1.0.3
26 ffmpeg_kit_flutter_full_gpl: ^4.5.1
27 wakelock: ^0.5.6
28@immutable
29class TranscodeUploadMessage {
30 const TranscodeUploadMessage({
31 required this.id,
32 required this.statusTitle,
33 required this.statusMessage,
34 required this.uploadPercentage,
35 required this.isRunning,
36 required this.completed,
37 required this.showSpinner,
38 required this.showPercentage,
39 required this.showError,
40 });
41
42 final int id;
43 final String statusTitle;
44 final String statusMessage;
45 final String uploadPercentage;
46 final bool isRunning;
47 final bool completed;
48 final bool showSpinner;
49 final bool showPercentage;
50 final bool showError;
51
52 TranscodeUploadMessage copyWith({
53 int? id,
54 String? statusTitle,
55 String? statusMessage,
56 String? uploadPercentage,
57 bool? isRunning,
58 bool? completed,
59 bool? showSpinner,
60 bool? showPercentage,
61 bool? showError,
62 }) {
63 return TranscodeUploadMessage(
64 id: id ?? this.id,
65 statusTitle: statusTitle ?? this.statusTitle,
66 statusMessage: statusMessage ?? this.statusMessage,
67 uploadPercentage: uploadPercentage ?? this.uploadPercentage,
68 isRunning: isRunning ?? this.isRunning,
69 completed: completed ?? this.completed,
70 showSpinner: showSpinner ?? this.showSpinner,
71 showPercentage: showSpinner ?? this.showPercentage,
72 showError: showError ?? this.showError,
73 );
74 }
75}
76
77class TranscodeUploadMessageNotifier
78 extends StateNotifier<List<TranscodeUploadMessage>> {
79 TranscodeUploadMessageNotifier() : super([]);
80
81 /// Since our state is immutable, we are not allowed to do
82 /// `state.add(message)`. Instead, we should create a new list of messages which
83 /// contains the previous items and the new one.
84 ///
85 /// Using Dart's spread operator here is helpful!
86 void set(TranscodeUploadMessage message) {
87 state = [...state, message];
88 }
89
90 /// Our state is immutable. So we're making a new list instead of changing
91 /// the existing list.
92 void remove(int id) {
93 state = [
94 for (final message in state)
95 if (message.id != id) message,
96 ];
97 }
98
99 /// Update message. Since our state is immutable, we need to make a copy of
100 /// the message. We're using our `copyWith` method implemented before to help
101 /// with that.
102 void update(TranscodeUploadMessage messageUpdated) {
103 state = [
104 for (final message in state)
105 if (message.id == messageUpdated.id)
106
107 /// Use copyWith to update a message
108 message.copyWith(
109 statusTitle: messageUpdated.statusTitle,
110 statusMessage: messageUpdated.statusMessage,
111 uploadPercentage: messageUpdated.uploadPercentage,
112 isRunning: messageUpdated.isRunning,
113 completed: messageUpdated.completed,
114 showSpinner: messageUpdated.showSpinner,
115 showPercentage: messageUpdated.showPercentage,
116 showError: messageUpdated.showError,
117 )
118 else
119
120 /// other messages, which there are not any at this time, are not
121 /// modified
122 message,
123 ];
124 }
125}
126
127/// Using StateNotifierProvider to allow the UI to interact with our
128/// TranscodeUploadMessageNotifier class.
129final transcodeMessageProvider = StateNotifierProvider.autoDispose<
130 TranscodeUploadMessageNotifier, List<TranscodeUploadMessage>>((ref) {
131 return TranscodeUploadMessageNotifier();
132});
133/// By default, set to video ffmpeg command.
134String ffmpegCommand = '-i $messageUri '
135 '-acodec aac '
136 '-vcodec libx264 '
137 '-f mp4 -preset veryfast '
138 '-movflags frag_keyframe+empty_moov '
139 '-crf 23 $newMessageUri';
140
141if (_recordingType == RecordingType.audio) {
142 ffmpegCommand = '-vn '
143 '-i $messageUri '
144 '-y '
145 '-acodec libmp3lame '
146 '-f '
147 'mp3 '
148 '$newMessageUri';
149}
150
151/// Set the initial state notifier as we start transcoding.
152ref
153.read(transcodeMessageProvider.notifier)
154.set(const TranscodeUploadMessage(
155 id: 1,
156 statusTitle: 'Transcoding Recording',
157 statusMessage: 'Your recording is being transcoded '
158 'before upload. Please do not navigate away from this screen.',
159 uploadPercentage: '0%',
160 isRunning: true,
161 completed: false,
162 showSpinner: false,
163 showPercentage: false,
164 showError: false,
165));
166
167await FFmpegKit.executeAsync(
168 ffmpegCommand,
169 (Session session) async {
170 final ReturnCode? returnCode = await session.getReturnCode();
171
172 if (ReturnCode.isSuccess(returnCode)) {
173 /// Transcoding is complete, now display uploading message
174 /// and spinner at 0%.
175 ref
176 .read(transcodeMessageProvider.notifier)
177 .update(const TranscodeUploadMessage(
178 id: 1,
179 statusTitle: 'Uploading Recording',
180 statusMessage:
181 'Your recording is now being '
182 'uploaded. Please do not navigate away from this screen.',
183 uploadPercentage: '0%',
184 isRunning: true,
185 completed: false,
186 showSpinner: true,
187 showPercentage: true,
188 showError: false,
189 ));
190
191 /// Upload the now transcoded video/audio to cloud storage where
192 /// Use flutterfire firebase storage tasks to get upload
193 /// progress. Your firebase storage function can also
194 /// reuse the transcodeMessageProvider to send UI state
195 /// updates for the upload, which will happen very quickly
196 /// even on slow connections now that the recording size
197 /// is dramatically reduced.
198 await uploadRecordingToFirebaseCloudStorage(ref);
199 } else if (ReturnCode.isCancel(returnCode)) {
200 // Do something if canceled
201 } else {
202 // Do something with the error
203 }
204 },
205 (Log log) => debugPrint(log.getMessage()),
206 (Statistics statistic) {
207 /// Statistics provides a running transcoding progress meter.
208 int completePercentage = (statistic.getTime() * 100) ~/ _duration!;
209 ref
210 .read(transcodeMessageProvider.notifier)
211 .update(TranscodeUploadMessage(
212 id: 1,
213 statusTitle: 'Transcoding Recording',
214 statusMessage: 'Your recording is being '
215 'transcoded. Please do not navigate away from this screen.',
216 uploadPercentage: '$completePercentage%',
217 isRunning: true,
218 completed: false,
219 showSpinner: true,
220 showPercentage: true,
221 showError: false,
222 ));
223 }).then((Session session) {
224debugPrint(
225 'Async FFmpeg process started with sessionId ${session.getSessionId()}.');
226}).catchError((error) async {
227debugPrint('transcoding error: $error');
228});
229
Use a Riverpod Consumer to update UI by watching the StateNotifier and displaying updated state in your UI.
1
2 Future<void> uploadTask({
3 required WidgetRef ref,
4 required File file,
5 required String objectPath,
6 SettableMetadata? metaData,
7 bool deleteAfterUpload = false,
8 bool displayProgressNotification = false,
9 }) async {
10 String filePath = file.path;
11 filename = basename(file.path);
12
13 /// Remove any instances of '//' from the path.
14 final String path = '$storagePath/$objectPath'.replaceAll('//', '/');
15 UploadTask task = storage.ref(path).putFile(
16 File(filePath),
17 metaData,
18 );
19
20 /// Store UploadTask in StateNotifierProvider to monitor progress.
21 ref.read(uploadingStateProvider.notifier).myUploadTask(task);
22 }
23dependencies:
24 camera: ^0.9.4+12
25 flutter_riverpod: ^1.0.3
26 ffmpeg_kit_flutter_full_gpl: ^4.5.1
27 wakelock: ^0.5.6
28@immutable
29class TranscodeUploadMessage {
30 const TranscodeUploadMessage({
31 required this.id,
32 required this.statusTitle,
33 required this.statusMessage,
34 required this.uploadPercentage,
35 required this.isRunning,
36 required this.completed,
37 required this.showSpinner,
38 required this.showPercentage,
39 required this.showError,
40 });
41
42 final int id;
43 final String statusTitle;
44 final String statusMessage;
45 final String uploadPercentage;
46 final bool isRunning;
47 final bool completed;
48 final bool showSpinner;
49 final bool showPercentage;
50 final bool showError;
51
52 TranscodeUploadMessage copyWith({
53 int? id,
54 String? statusTitle,
55 String? statusMessage,
56 String? uploadPercentage,
57 bool? isRunning,
58 bool? completed,
59 bool? showSpinner,
60 bool? showPercentage,
61 bool? showError,
62 }) {
63 return TranscodeUploadMessage(
64 id: id ?? this.id,
65 statusTitle: statusTitle ?? this.statusTitle,
66 statusMessage: statusMessage ?? this.statusMessage,
67 uploadPercentage: uploadPercentage ?? this.uploadPercentage,
68 isRunning: isRunning ?? this.isRunning,
69 completed: completed ?? this.completed,
70 showSpinner: showSpinner ?? this.showSpinner,
71 showPercentage: showSpinner ?? this.showPercentage,
72 showError: showError ?? this.showError,
73 );
74 }
75}
76
77class TranscodeUploadMessageNotifier
78 extends StateNotifier<List<TranscodeUploadMessage>> {
79 TranscodeUploadMessageNotifier() : super([]);
80
81 /// Since our state is immutable, we are not allowed to do
82 /// `state.add(message)`. Instead, we should create a new list of messages which
83 /// contains the previous items and the new one.
84 ///
85 /// Using Dart's spread operator here is helpful!
86 void set(TranscodeUploadMessage message) {
87 state = [...state, message];
88 }
89
90 /// Our state is immutable. So we're making a new list instead of changing
91 /// the existing list.
92 void remove(int id) {
93 state = [
94 for (final message in state)
95 if (message.id != id) message,
96 ];
97 }
98
99 /// Update message. Since our state is immutable, we need to make a copy of
100 /// the message. We're using our `copyWith` method implemented before to help
101 /// with that.
102 void update(TranscodeUploadMessage messageUpdated) {
103 state = [
104 for (final message in state)
105 if (message.id == messageUpdated.id)
106
107 /// Use copyWith to update a message
108 message.copyWith(
109 statusTitle: messageUpdated.statusTitle,
110 statusMessage: messageUpdated.statusMessage,
111 uploadPercentage: messageUpdated.uploadPercentage,
112 isRunning: messageUpdated.isRunning,
113 completed: messageUpdated.completed,
114 showSpinner: messageUpdated.showSpinner,
115 showPercentage: messageUpdated.showPercentage,
116 showError: messageUpdated.showError,
117 )
118 else
119
120 /// other messages, which there are not any at this time, are not
121 /// modified
122 message,
123 ];
124 }
125}
126
127/// Using StateNotifierProvider to allow the UI to interact with our
128/// TranscodeUploadMessageNotifier class.
129final transcodeMessageProvider = StateNotifierProvider.autoDispose<
130 TranscodeUploadMessageNotifier, List<TranscodeUploadMessage>>((ref) {
131 return TranscodeUploadMessageNotifier();
132});
133/// By default, set to video ffmpeg command.
134String ffmpegCommand = '-i $messageUri '
135 '-acodec aac '
136 '-vcodec libx264 '
137 '-f mp4 -preset veryfast '
138 '-movflags frag_keyframe+empty_moov '
139 '-crf 23 $newMessageUri';
140
141if (_recordingType == RecordingType.audio) {
142 ffmpegCommand = '-vn '
143 '-i $messageUri '
144 '-y '
145 '-acodec libmp3lame '
146 '-f '
147 'mp3 '
148 '$newMessageUri';
149}
150
151/// Set the initial state notifier as we start transcoding.
152ref
153.read(transcodeMessageProvider.notifier)
154.set(const TranscodeUploadMessage(
155 id: 1,
156 statusTitle: 'Transcoding Recording',
157 statusMessage: 'Your recording is being transcoded '
158 'before upload. Please do not navigate away from this screen.',
159 uploadPercentage: '0%',
160 isRunning: true,
161 completed: false,
162 showSpinner: false,
163 showPercentage: false,
164 showError: false,
165));
166
167await FFmpegKit.executeAsync(
168 ffmpegCommand,
169 (Session session) async {
170 final ReturnCode? returnCode = await session.getReturnCode();
171
172 if (ReturnCode.isSuccess(returnCode)) {
173 /// Transcoding is complete, now display uploading message
174 /// and spinner at 0%.
175 ref
176 .read(transcodeMessageProvider.notifier)
177 .update(const TranscodeUploadMessage(
178 id: 1,
179 statusTitle: 'Uploading Recording',
180 statusMessage:
181 'Your recording is now being '
182 'uploaded. Please do not navigate away from this screen.',
183 uploadPercentage: '0%',
184 isRunning: true,
185 completed: false,
186 showSpinner: true,
187 showPercentage: true,
188 showError: false,
189 ));
190
191 /// Upload the now transcoded video/audio to cloud storage where
192 /// Use flutterfire firebase storage tasks to get upload
193 /// progress. Your firebase storage function can also
194 /// reuse the transcodeMessageProvider to send UI state
195 /// updates for the upload, which will happen very quickly
196 /// even on slow connections now that the recording size
197 /// is dramatically reduced.
198 await uploadRecordingToFirebaseCloudStorage(ref);
199 } else if (ReturnCode.isCancel(returnCode)) {
200 // Do something if canceled
201 } else {
202 // Do something with the error
203 }
204 },
205 (Log log) => debugPrint(log.getMessage()),
206 (Statistics statistic) {
207 /// Statistics provides a running transcoding progress meter.
208 int completePercentage = (statistic.getTime() * 100) ~/ _duration!;
209 ref
210 .read(transcodeMessageProvider.notifier)
211 .update(TranscodeUploadMessage(
212 id: 1,
213 statusTitle: 'Transcoding Recording',
214 statusMessage: 'Your recording is being '
215 'transcoded. Please do not navigate away from this screen.',
216 uploadPercentage: '$completePercentage%',
217 isRunning: true,
218 completed: false,
219 showSpinner: true,
220 showPercentage: true,
221 showError: false,
222 ));
223 }).then((Session session) {
224debugPrint(
225 'Async FFmpeg process started with sessionId ${session.getSessionId()}.');
226}).catchError((error) async {
227debugPrint('transcoding error: $error');
228});
229Consumer(
230 builder: (context, watch, child) {
231 final List<TranscodeUploadMessage> messages =
232 ref.watch(transcodeMessageProvider);
233 if (messages.isEmpty) {
234 return const SizedBox.shrink();
235 }
236
237 final message = messages[0];
238
239 if (message.isRunning ||
240 message.completed ||
241 message.showError) {
242 // Display widgets with StateNotifier data
243 }
244
245 return const SizedBox.shrink();
246 },
247)
248
Community Discussions contain sources that include Stack Exchange Network
Tutorials and Learning Resources in Wifi
Tutorials and Learning Resources are not available at this moment for Wifi