mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 06:39:11 +00:00
74_AutomowerConnect.pm: calculation of mowing area hull polygon, some more info in InternalData and StatisticsData, downloading of third party library for hull calculation
git-svn-id: https://svn.fhem.de/fhem/trunk@28772 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
efef531fec
commit
781cd40464
@ -1,5 +1,8 @@
|
|||||||
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
||||||
# Do not insert empty lines here, update check depends on it
|
# Do not insert empty lines here, update check depends on it
|
||||||
|
- feature: 74_AutomowerConnect.pm: calculation of mowing area hull polygon,
|
||||||
|
some more info in InternalData and StatisticsData,
|
||||||
|
downloading of third party library for hull calculation
|
||||||
- feature: 76_SolarForecast: options for conprice, feedprice and more
|
- feature: 76_SolarForecast: options for conprice, feedprice and more
|
||||||
- change: 49_SSCam: check SSChatBot/TelegramBot is disabled before send data
|
- change: 49_SSCam: check SSChatBot/TelegramBot is disabled before send data
|
||||||
- feature: 70_PylonLowVoltage: add specific Alarm readings, support of US5000
|
- feature: 70_PylonLowVoltage: add specific Alarm readings, support of US5000
|
||||||
|
@ -74,6 +74,7 @@ sub Initialize() {
|
|||||||
"mowerCuttingWidth " .
|
"mowerCuttingWidth " .
|
||||||
"mowerSchedule:textField-long " .
|
"mowerSchedule:textField-long " .
|
||||||
"mowingAreaLimits:textField-long " .
|
"mowingAreaLimits:textField-long " .
|
||||||
|
"mowingAreaHull:textField-long " .
|
||||||
"propertyLimits:textField-long " .
|
"propertyLimits:textField-long " .
|
||||||
"weekdaysToResetWayPoints " .
|
"weekdaysToResetWayPoints " .
|
||||||
"numberOfWayPointsToDisplay " .
|
"numberOfWayPointsToDisplay " .
|
||||||
@ -81,7 +82,8 @@ sub Initialize() {
|
|||||||
"addPositionPolling:1,0 " .
|
"addPositionPolling:1,0 " .
|
||||||
$::readingFnAttributes;
|
$::readingFnAttributes;
|
||||||
|
|
||||||
$::data{FWEXT}{AutomowerConnect}{SCRIPT} = "automowerconnect.js";
|
$::data{FWEXT}{AutomowerConnect}{SCRIPT} = 'automowerconnect.js';
|
||||||
|
$::data{FWEXT}{AutomowerConnectA}{SCRIPT} = '/automowerconnect/hull.js';
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
@ -101,6 +103,7 @@ __END__
|
|||||||
|
|
||||||
=begin html
|
=begin html
|
||||||
|
|
||||||
|
<a id="74_AutomowerConnect.pm" ></a>
|
||||||
<a id="AutomowerConnect" ></a>
|
<a id="AutomowerConnect" ></a>
|
||||||
<h3>AutomowerConnect</h3>
|
<h3>AutomowerConnect</h3>
|
||||||
<ul>
|
<ul>
|
||||||
@ -126,6 +129,9 @@ __END__
|
|||||||
<li>To get access to the API an application has to be created in the <a target="_blank" href="https://developer.husqvarnagroup.cloud/docs/get-started">Husqvarna Developer Portal</a>. The application has to be connected with the AutomowerConnect API.</li>
|
<li>To get access to the API an application has to be created in the <a target="_blank" href="https://developer.husqvarnagroup.cloud/docs/get-started">Husqvarna Developer Portal</a>. The application has to be connected with the AutomowerConnect API.</li>
|
||||||
<li>During registration an application key (client_id) and an application secret (client secret) is provided. Use these for the module.</li>
|
<li>During registration an application key (client_id) and an application secret (client secret) is provided. Use these for the module.</li>
|
||||||
<li>The module uses client credentials as grant type for authorization.</li>
|
<li>The module uses client credentials as grant type for authorization.</li>
|
||||||
|
<br>
|
||||||
|
<li>The module downloads third party software from external server necessary to calculate the hull of mowing area.</li>
|
||||||
|
<br>
|
||||||
</ul>
|
</ul>
|
||||||
<br>
|
<br>
|
||||||
<a id="AutomowerConnectDefine"></a>
|
<a id="AutomowerConnectDefine"></a>
|
||||||
@ -188,6 +194,10 @@ __END__
|
|||||||
<code>set <name> cuttingHeight <1..9></code><br>
|
<code>set <name> cuttingHeight <1..9></code><br>
|
||||||
Sets the cutting height. NOTE: Do not use for 550 EPOS and Ceora.</li>
|
Sets the cutting height. NOTE: Do not use for 550 EPOS and Ceora.</li>
|
||||||
|
|
||||||
|
<li><a id='AutomowerConnect-set-cuttingHeightInWorkArea'>cuttingHeightInWorkArea</a><br>
|
||||||
|
<code>set <name> cuttingHeightInWorkArea <Id|name> <0..100></code><br>
|
||||||
|
Testing: Sets the cutting height for Id or zone name from 0 to 100. Zone name must not include space and contain at least one alphabetic character.</li>
|
||||||
|
|
||||||
<li><a id='AutomowerConnect-set-stayOutZone_enable'>stayOutZone_enable</a><br>
|
<li><a id='AutomowerConnect-set-stayOutZone_enable'>stayOutZone_enable</a><br>
|
||||||
<code>set <name> stayOutZone_enable <Id|name></code><br>
|
<code>set <name> stayOutZone_enable <Id|name></code><br>
|
||||||
Testing: Enables stay out zone by Id or zone name. Zone name must not include space and contain at least one alphabetic character.</li>
|
Testing: Enables stay out zone by Id or zone name. Zone name must not include space and contain at least one alphabetic character.</li>
|
||||||
@ -283,14 +293,51 @@ __END__
|
|||||||
|
|
||||||
<li><a id='AutomowerConnect-attr-mapDesignAttributes'>mapDesignAttributes</a><br>
|
<li><a id='AutomowerConnect-attr-mapDesignAttributes'>mapDesignAttributes</a><br>
|
||||||
<code>attr <name> mapDesignAttributes <complete list of design-attributes></code><br>
|
<code>attr <name> mapDesignAttributes <complete list of design-attributes></code><br>
|
||||||
Load the list of attributes by <code>set <name> defaultDesignAttributesToAttribute</code> to change its values. Some default values are
|
Load the list of attributes by <code>set <name> defaultDesignAttributesToAttribute</code> to change its values. Design attributes with changed default values are mandatory in this attribute.<br>
|
||||||
|
Default values:
|
||||||
<ul>
|
<ul>
|
||||||
<li>mower path for activity MOWING: red</li>
|
<code>
|
||||||
<li>path in CS, activity CHARGING,PARKED_IN_CS: grey</li>
|
areaLimitsColor="#ff8000"<br>
|
||||||
<li>path for activity LEAVING: green</li>
|
areaLimitsLineWidth="1"<br>
|
||||||
<li>path for activity GOING_HOME: blue</li>
|
areaLimitsConnector=""<br>
|
||||||
<li>path for interval with error (all activities with error): kind of magenta</li>
|
hullColor="#0066ff"<br>
|
||||||
<li>all other activities: grey</li>
|
hullLineWidth="1"<br>
|
||||||
|
hullConnector="1"<br>
|
||||||
|
hullResolution="40"<br>
|
||||||
|
hullCalculate=""<br>
|
||||||
|
propertyLimitsColor="#33cc33"<br>
|
||||||
|
propertyLimitsLineWidth="1"<br>
|
||||||
|
propertyLimitsConnector="1"<br>
|
||||||
|
errorBackgroundColor="#3d3d3d"<br>
|
||||||
|
errorFont="14px Courier New"<br>
|
||||||
|
errorFontColor="#ff8000"<br>
|
||||||
|
errorPathLineColor="#ff00bf"<br>
|
||||||
|
errorPathLineDash=""<br>
|
||||||
|
errorPathLineWidth="2"<br>
|
||||||
|
chargingStationPathLineColor="#999999"<br>
|
||||||
|
chargingStationPathLineDash="6,2"<br>
|
||||||
|
chargingStationPathLineWidth="1"<br>
|
||||||
|
chargingStationPathDotWidth="2"<br>
|
||||||
|
otherActivityPathLineColor="#999999"<br>
|
||||||
|
otherActivityPathLineDash="6,2"<br>
|
||||||
|
otherActivityPathLineWidth="1"<br>
|
||||||
|
otherActivityPathDotWidth="4"<br>
|
||||||
|
leavingPathLineColor="#33cc33"<br>
|
||||||
|
leavingPathLineDash="6,2"<br>
|
||||||
|
leavingPathLineWidth="2"<br>
|
||||||
|
leavingPathDotWidth="4"<br>
|
||||||
|
goingHomePathLineColor="#0099ff"<br>
|
||||||
|
goingHomePathLineDash="6,2"<br>
|
||||||
|
goingHomePathLineWidth="2"<br>
|
||||||
|
goingHomePathDotWidth="4"<br>
|
||||||
|
mowingPathDisplayStart=""<br>
|
||||||
|
mowingPathLineColor="#ff0000"<br>
|
||||||
|
mowingPathLineDash="6,2"<br>
|
||||||
|
mowingPathLineWidth="1"<br>
|
||||||
|
mowingPathDotWidth="2"<br>
|
||||||
|
mowingPathUseDots=""<br>
|
||||||
|
mowingPathShowCollisions=""
|
||||||
|
</code>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
@ -402,6 +449,18 @@ __END__
|
|||||||
<code>attr <name> addPositionPolling <[1|<b>0</b>]></code><br>
|
<code>attr <name> addPositionPolling <[1|<b>0</b>]></code><br>
|
||||||
Set position polling, default 0 (no position polling). Gets periodically position data from mower, instead from websocket. It has no effect without setting attribute addPollingMinInterval.</li>
|
Set position polling, default 0 (no position polling). Gets periodically position data from mower, instead from websocket. It has no effect without setting attribute addPollingMinInterval.</li>
|
||||||
|
|
||||||
|
<li><a id='AutomowerConnect-attr-mowingAreaHull'>mowingAreaHull</a><br>
|
||||||
|
<code>attr <name> mowingAreaHull <use button 'mowingAreaHullToAttribute' to fill the attribute></code><br>
|
||||||
|
Contains the calculated hull coordinates as JSON string and is set by button 'mowingAreaHullToAttribute' under the dislpayed map.<br>
|
||||||
|
The stored hull polygon is displayed like the other limits.<br>
|
||||||
|
Use the design attribute 'hullResolution' to change the number of fractions ℕ<sub>0</sub><br>.
|
||||||
|
The hull polygon is calculated when the design attribut is set to 1 <code>hullCalculate="1"</code> and there are more than 50 Points for activity MOWING.<br>
|
||||||
|
The calculation is done only after site reload.<br>
|
||||||
|
The calculation of hull is stopped when the attribute ist set and starts again when attribute is deleted.<br>
|
||||||
|
The attribute <code>weekdaysToResetWayPoints</code> should be set to - and also the design attribute <code>mowingPathUseDots</code> should be set to "1" until the hull is sufficient.
|
||||||
|
|
||||||
|
</li>
|
||||||
|
|
||||||
<li><a href="disable">disable</a></li>
|
<li><a href="disable">disable</a></li>
|
||||||
|
|
||||||
<li><a href="disabledForIntervals">disabledForIntervals</a></li>
|
<li><a href="disabledForIntervals">disabledForIntervals</a></li>
|
||||||
@ -470,6 +529,7 @@ __END__
|
|||||||
<li>status_statusTimestamp - local time of last status update</li>
|
<li>status_statusTimestamp - local time of last status update</li>
|
||||||
<li>status_statusTimestampDiff - time difference in seconds between the last and second last status update</li>
|
<li>status_statusTimestampDiff - time difference in seconds between the last and second last status update</li>
|
||||||
<li>system_name - name of the mower</li>
|
<li>system_name - name of the mower</li>
|
||||||
|
<li>third_party_library - notice about downloaded JS library. Deleting the reading has no side effects.</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
@ -506,6 +566,8 @@ __END__
|
|||||||
<li>Für den Zugriff auf die API muss eine Application im <a target="_blank" href="https://developer.husqvarnagroup.cloud/docs/get-started">Husqvarna Developer Portal</a> angelegt und mit der Automower Connect API verbunden werden.</li>
|
<li>Für den Zugriff auf die API muss eine Application im <a target="_blank" href="https://developer.husqvarnagroup.cloud/docs/get-started">Husqvarna Developer Portal</a> angelegt und mit der Automower Connect API verbunden werden.</li>
|
||||||
<li>Währenddessen wird ein Application Key (client_id) und ein Application Secret (client secret) bereitgestellt. Diese Angaben sind im Zusammenhang mit der Definition eines Gerätes erforderlich.</li>
|
<li>Währenddessen wird ein Application Key (client_id) und ein Application Secret (client secret) bereitgestellt. Diese Angaben sind im Zusammenhang mit der Definition eines Gerätes erforderlich.</li>
|
||||||
<li>Das Modul nutzt Client Credentials als Granttype zur Authorisierung.</li>
|
<li>Das Modul nutzt Client Credentials als Granttype zur Authorisierung.</li>
|
||||||
|
<br>
|
||||||
|
<li>Das Modul läd Drittsoftware, die zur Berechnung der Hüllkurve des Mähbereiches erforderlich ist, von einem externem Server.</li>
|
||||||
<br>
|
<br>
|
||||||
</ul>
|
</ul>
|
||||||
<br>
|
<br>
|
||||||
@ -565,6 +627,10 @@ __END__
|
|||||||
<code>set <name> cuttingHeight <1..9></code><br>
|
<code>set <name> cuttingHeight <1..9></code><br>
|
||||||
Setzt die Schnitthöhe. HINWEIS: Nicht für 550 EPOS und Ceora geeignet.</li>
|
Setzt die Schnitthöhe. HINWEIS: Nicht für 550 EPOS und Ceora geeignet.</li>
|
||||||
|
|
||||||
|
<li><a id='AutomowerConnect-set-cuttingHeightInWorkArea'>cuttingHeightInWorkArea</a><br>
|
||||||
|
<code>set <name> cuttingHeightInWorkArea <Id|name> <0..100></code><br>
|
||||||
|
Testing: Setzt die Schnitthöhe für Id oder Zonennamen von 0 bis 100. Der Zonenname darf keine Leerzeichen beinhalten und muss mindestens einen Buchstaben enthalten.</li>
|
||||||
|
|
||||||
<li><a id='AutomowerConnect-set-stayOutZone_enable'>stayOutZone_enable</a><br>
|
<li><a id='AutomowerConnect-set-stayOutZone_enable'>stayOutZone_enable</a><br>
|
||||||
<code>set <name> stayOutZone_enable <Id|zone name></code><br>
|
<code>set <name> stayOutZone_enable <Id|zone name></code><br>
|
||||||
Testing: Enabled stayOutZone für die Id oder den Namen der Zone, er darf keine Leerzeichen beinhalten und muss mindestens einen Buchstaben enthalten.</li>
|
Testing: Enabled stayOutZone für die Id oder den Namen der Zone, er darf keine Leerzeichen beinhalten und muss mindestens einen Buchstaben enthalten.</li>
|
||||||
@ -664,14 +730,51 @@ __END__
|
|||||||
|
|
||||||
<li><a id='AutomowerConnect-attr-mapDesignAttributes'>mapDesignAttributes</a><br>
|
<li><a id='AutomowerConnect-attr-mapDesignAttributes'>mapDesignAttributes</a><br>
|
||||||
<code>attr <name> mapDesignAttributes <complete list of design-attributes></code><br>
|
<code>attr <name> mapDesignAttributes <complete list of design-attributes></code><br>
|
||||||
Lade die Attributliste mit <code>set <name> defaultDesignAttributesToAttribute</code> um die Werte zu ändern. Einige Vorgabewerte:
|
Lade die Attributliste mit <code>set <name> defaultDesignAttributesToAttribute</code> um die Werte zu ändern. Nur Designattribute mit geänderten Standartwerten müssen in diesem Attribut enthalten sein.<br>
|
||||||
|
Vorgabe Werte:
|
||||||
<ul>
|
<ul>
|
||||||
<li>Pfad beim mähen, Aktivität MOWING: rot</li>
|
<code>
|
||||||
<li>In der Ladestation, Aktivität CHARGING,PARKED_IN_CS: grau</li>
|
areaLimitsColor="#ff8000"<br>
|
||||||
<li>Pfad für die Aktivität LEAVING: grün</li>
|
areaLimitsLineWidth="1"<br>
|
||||||
<li>Pfad für Aktivität GOING_HOME: blau</li>
|
areaLimitsConnector=""<br>
|
||||||
<li>Pfad eines Intervalls mit Fehler (alle Aktivitäten with error): Eine Art Magenta</li>
|
hullColor="#0066ff"<br>
|
||||||
<li>Pfad aller anderen Aktivitäten: grau</li>
|
hullLineWidth="1"<br>
|
||||||
|
hullConnector="1"<br>
|
||||||
|
hullResolution="40"<br>
|
||||||
|
hullCalculate=""<br>
|
||||||
|
propertyLimitsColor="#33cc33"<br>
|
||||||
|
propertyLimitsLineWidth="1"<br>
|
||||||
|
propertyLimitsConnector="1"<br>
|
||||||
|
errorBackgroundColor="#3d3d3d"<br>
|
||||||
|
errorFont="14px Courier New"<br>
|
||||||
|
errorFontColor="#ff8000"<br>
|
||||||
|
errorPathLineColor="#ff00bf"<br>
|
||||||
|
errorPathLineDash=""<br>
|
||||||
|
errorPathLineWidth="2"<br>
|
||||||
|
chargingStationPathLineColor="#999999"<br>
|
||||||
|
chargingStationPathLineDash="6,2"<br>
|
||||||
|
chargingStationPathLineWidth="1"<br>
|
||||||
|
chargingStationPathDotWidth="2"<br>
|
||||||
|
otherActivityPathLineColor="#999999"<br>
|
||||||
|
otherActivityPathLineDash="6,2"<br>
|
||||||
|
otherActivityPathLineWidth="1"<br>
|
||||||
|
otherActivityPathDotWidth="4"<br>
|
||||||
|
leavingPathLineColor="#33cc33"<br>
|
||||||
|
leavingPathLineDash="6,2"<br>
|
||||||
|
leavingPathLineWidth="2"<br>
|
||||||
|
leavingPathDotWidth="4"<br>
|
||||||
|
goingHomePathLineColor="#0099ff"<br>
|
||||||
|
goingHomePathLineDash="6,2"<br>
|
||||||
|
goingHomePathLineWidth="2"<br>
|
||||||
|
goingHomePathDotWidth="4"<br>
|
||||||
|
mowingPathDisplayStart=""<br>
|
||||||
|
mowingPathLineColor="#ff0000"<br>
|
||||||
|
mowingPathLineDash="6,2"<br>
|
||||||
|
mowingPathLineWidth="1"<br>
|
||||||
|
mowingPathDotWidth="2"<br>
|
||||||
|
mowingPathUseDots=""<br>
|
||||||
|
mowingPathShowCollisions=""
|
||||||
|
</code>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
@ -787,6 +890,17 @@ __END__
|
|||||||
<code>attr <name> addPositionPolling <[1|<b>0</b>]></code><br>
|
<code>attr <name> addPositionPolling <[1|<b>0</b>]></code><br>
|
||||||
Setzt das Positionspolling, default 0 (kein Positionpolling). Liest periodisch Positiondaten des Mähers, an Stelle der über Websocket gelieferten Daten. Das Attribut ist nur wirksam, wenn durch das Attribut addPollingMinInterval das Polling eingeschaltet ist.</li>
|
Setzt das Positionspolling, default 0 (kein Positionpolling). Liest periodisch Positiondaten des Mähers, an Stelle der über Websocket gelieferten Daten. Das Attribut ist nur wirksam, wenn durch das Attribut addPollingMinInterval das Polling eingeschaltet ist.</li>
|
||||||
|
|
||||||
|
<li><a id='AutomowerConnect-attr-mowingAreaHull'>mowingAreaHull</a><br>
|
||||||
|
<code>attr <name> mowingAreaHull <use button 'mowingAreaHullToAttribute' to fill the attribute></code><br><br>
|
||||||
|
Enthält die berechneten Hüllenkooordinaten als JSON String und wird gesetzt durch den Button 'mowingAreaHullToAttribute' unterhalb der angezeigten Karte.<br>
|
||||||
|
Das gespeicherte Hüllenpolygon wird wie die anderen Grenzen angezeigt.<br>
|
||||||
|
Mit dem Designattribut 'hullResolution' kann die Anzahl der Brechungen beeinflusst werden ℕ<sub>0</sub>, Default 40.<br>
|
||||||
|
Das Hüllenpolygon wird berechnet wenn das Designattribute gesetzt ist, <code>hullCalculate="1"</code> und es mehr als 50 Wegpunkte der Aktivität MOWING gibt.<br>
|
||||||
|
Die Berechnung wird beim Laden oder Wiederladen der Website ausgeführt.<br>
|
||||||
|
Die Berechnung stopt wenn dieses Attribut gesetzt ist und startet wenn das Attibut gelöst wird.<br>
|
||||||
|
Das Attribut <code>weekdaysToResetWayPoints</code> sollte auf <code>-</code> und das Designattribut <code>mowingPathUseDots</code> sollte auf <code>"1"</code> gesetzt werden, bis das Polygon die Hülle der Mähfläche zufriedenstellend abbildet.
|
||||||
|
</li>
|
||||||
|
|
||||||
<li><a href="disable">disable</a></li>
|
<li><a href="disable">disable</a></li>
|
||||||
|
|
||||||
<li><a href="disabledForIntervals">disabledForIntervals</a></li>
|
<li><a href="disabledForIntervals">disabledForIntervals</a></li>
|
||||||
@ -855,6 +969,7 @@ __END__
|
|||||||
<li>status_statusTimestamp - Lokalzeit des letzten Statusupdates in der API</li>
|
<li>status_statusTimestamp - Lokalzeit des letzten Statusupdates in der API</li>
|
||||||
<li>status_statusTimestampDiff - Zeitdifferenz zwischen dem letzten und vorletzten Statusupdate.</li>
|
<li>status_statusTimestampDiff - Zeitdifferenz zwischen dem letzten und vorletzten Statusupdate.</li>
|
||||||
<li>system_name - Name des Automowers</li>
|
<li>system_name - Name des Automowers</li>
|
||||||
|
<li>third_party_library - Info, dass die JS-Bibliothek geladen wurde. Das Reading kann bedenkenlos gelöscht werden.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
@ -420,7 +420,6 @@ FHEM/74_UnifiClient.pm wuehler Automatisierung
|
|||||||
FHEM/74_UnifiProtect.pm justme1968 Sonstiges
|
FHEM/74_UnifiProtect.pm justme1968 Sonstiges
|
||||||
FHEM/74_UnifiVideo.pm justme1968 Sonstiges
|
FHEM/74_UnifiVideo.pm justme1968 Sonstiges
|
||||||
FHEM/74_XiaomiBTLESens.pm CoolTux Sonstige Systeme
|
FHEM/74_XiaomiBTLESens.pm CoolTux Sonstige Systeme
|
||||||
FHEM/75_AutomowerConnectDevice.pm Ellert Sonstige Systeme https://forum.fhem.de/index.php/topic,131661.0.html
|
|
||||||
FHEM/75_MSG.pm loredo Automatisierung
|
FHEM/75_MSG.pm loredo Automatisierung
|
||||||
FHEM/75_msgConfig.pm loredo Automatisierung
|
FHEM/75_msgConfig.pm loredo Automatisierung
|
||||||
FHEM/76_msgDialog.pm orphan/Beta-User Frontends/Sprachsteuerung https://forum.fhem.de/index.php/topic,125710.0.html
|
FHEM/76_msgDialog.pm orphan/Beta-User Frontends/Sprachsteuerung https://forum.fhem.de/index.php/topic,125710.0.html
|
||||||
|
@ -48,6 +48,8 @@ BEGIN {
|
|||||||
CommandDeleteReading
|
CommandDeleteReading
|
||||||
FmtDateTime
|
FmtDateTime
|
||||||
FW_ME
|
FW_ME
|
||||||
|
FW_dir
|
||||||
|
FW_wname
|
||||||
getKeyValue
|
getKeyValue
|
||||||
InternalTimer
|
InternalTimer
|
||||||
InternalVal
|
InternalVal
|
||||||
@ -123,9 +125,14 @@ sub Define{
|
|||||||
$client_id =$val[2];
|
$client_id =$val[2];
|
||||||
$mowerNumber = $val[3] ? $val[3] : 0;
|
$mowerNumber = $val[3] ? $val[3] : 0;
|
||||||
|
|
||||||
my $mapAttr = 'areaLimitsColor="#ff8000"
|
my $mapAttr = 'areaLimitsColor="#ff8000"
|
||||||
areaLimitsLineWidth="1"
|
areaLimitsLineWidth="1"
|
||||||
areaLimitsConnector=""
|
areaLimitsConnector=""
|
||||||
|
hullColor="#0066ff"
|
||||||
|
hullLineWidth="1"
|
||||||
|
hullConnector="1"
|
||||||
|
hullResolution="40"
|
||||||
|
hullCalculate=""
|
||||||
propertyLimitsColor="#33cc33"
|
propertyLimitsColor="#33cc33"
|
||||||
propertyLimitsLineWidth="1"
|
propertyLimitsLineWidth="1"
|
||||||
propertyLimitsConnector="1"
|
propertyLimitsConnector="1"
|
||||||
@ -160,7 +167,7 @@ mowingPathUseDots=""
|
|||||||
mowingPathShowCollisions=""
|
mowingPathShowCollisions=""
|
||||||
';
|
';
|
||||||
|
|
||||||
my $mapZonesTpl = '{
|
my $mapZonesTpl = '{
|
||||||
"01_oben" : {
|
"01_oben" : {
|
||||||
"condition" : "$latitude > 52.6484600648553 || $longitude > 9.54799477359984 && $latitude > 52.64839739580418",
|
"condition" : "$latitude > 52.6484600648553 || $longitude > 9.54799477359984 && $latitude > 52.64839739580418",
|
||||||
"cuttingHeight" : "7"
|
"cuttingHeight" : "7"
|
||||||
@ -169,11 +176,19 @@ my $mapZonesTpl = '{
|
|||||||
"condition" : "undef",
|
"condition" : "undef",
|
||||||
"cuttingHeight" : "3"
|
"cuttingHeight" : "3"
|
||||||
}
|
}
|
||||||
}';
|
}';
|
||||||
|
|
||||||
|
my ( $path, $file) = $::data{FWEXT}{AutomowerConnectA}{SCRIPT} =~ /\/(.*)\/(.*)/;
|
||||||
|
|
||||||
|
|
||||||
%$hash = (%$hash,
|
%$hash = (%$hash,
|
||||||
helper => {
|
helper => {
|
||||||
passObj => FHEM::Core::Authentication::Passwords->new($type),
|
passObj => FHEM::Core::Authentication::Passwords->new($type),
|
||||||
|
FWEXTA => {
|
||||||
|
path => $path,
|
||||||
|
file => $file,
|
||||||
|
url => 'https://raw.githubusercontent.com/AndriiHeonia/hull/master/dist/hull.js'
|
||||||
|
},
|
||||||
interval => 840,
|
interval => 840,
|
||||||
interval_ws => 7110,
|
interval_ws => 7110,
|
||||||
interval_ping => 570,
|
interval_ping => 570,
|
||||||
@ -295,7 +310,10 @@ my $mapZonesTpl = '{
|
|||||||
currentWeekTime => 0,
|
currentWeekTime => 0,
|
||||||
lastWeekTrack => 0,
|
lastWeekTrack => 0,
|
||||||
lastWeekArea => 0,
|
lastWeekArea => 0,
|
||||||
lastWeekTime => 0
|
lastWeekTime => 0,
|
||||||
|
propertyArea => 0,
|
||||||
|
mowingArea => 0,
|
||||||
|
hullArea => 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -326,6 +344,10 @@ my $mapZonesTpl = '{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $url = $hash->{helper}{FWEXTA}{url};
|
||||||
|
mkdir( "$FW_dir/$path" ) if ( ! -d "$FW_dir/$path" );
|
||||||
|
getTpFile( $hash, $url, "$FW_dir/$path", $file ) if ( ! -e "$FW_dir/$path/$file");
|
||||||
|
|
||||||
if( $hash->{helper}->{passObj}->getReadPassword($name) ) {
|
if( $hash->{helper}->{passObj}->getReadPassword($name) ) {
|
||||||
|
|
||||||
RemoveInternalTimer($hash);
|
RemoveInternalTimer($hash);
|
||||||
@ -373,7 +395,10 @@ sub Delete {
|
|||||||
my $type = $hash->{TYPE};
|
my $type = $hash->{TYPE};
|
||||||
my $iam ="$type $name Delete: ";
|
my $iam ="$type $name Delete: ";
|
||||||
Log3( $name, 5, "$iam called" );
|
Log3( $name, 5, "$iam called" );
|
||||||
|
if ( scalar devspec2array( "TYPE=$type" ) == 1 ) {
|
||||||
|
delete $::data{FWEXT}{AutomowerConnect};
|
||||||
|
delete $::data{FWEXT}{AutomowerConnectA};
|
||||||
|
}
|
||||||
my ($passResp,$passErr) = $hash->{helper}->{passObj}->setDeletePassword($name);
|
my ($passResp,$passErr) = $hash->{helper}->{passObj}->setDeletePassword($name);
|
||||||
Log3( $name, 1, "$iam error: $passErr" ) if ($passErr);
|
Log3( $name, 1, "$iam error: $passErr" ) if ($passErr);
|
||||||
|
|
||||||
@ -463,9 +488,8 @@ sub FW_detailFn {
|
|||||||
my $zoom=AttrVal( $name,"mapImageZoom", 0.7 );
|
my $zoom=AttrVal( $name,"mapImageZoom", 0.7 );
|
||||||
my $backgroundcolor = AttrVal($name, 'mapBackgroundColor','');
|
my $backgroundcolor = AttrVal($name, 'mapBackgroundColor','');
|
||||||
my $bgstyle = $backgroundcolor ? " background-color:$backgroundcolor;" : '';
|
my $bgstyle = $backgroundcolor ? " background-color:$backgroundcolor;" : '';
|
||||||
my $design = AttrVal( $name, 'mapDesignAttributes', $hash->{helper}{mapdesign} );
|
|
||||||
my @adesign = split(/\R/,$design);
|
my $mapDesign = getDesignAttr( $hash );
|
||||||
my $mapDesign = 'data-'.join("data-",@adesign);
|
|
||||||
|
|
||||||
my ($picx,$picy) = AttrVal( $name,"mapImageWidthHeight", $hash->{helper}{imageWidthHeight} ) =~ /(\d+)\s(\d+)/;
|
my ($picx,$picy) = AttrVal( $name,"mapImageWidthHeight", $hash->{helper}{imageWidthHeight} ) =~ /(\d+)\s(\d+)/;
|
||||||
$picx=int($picx*$zoom);
|
$picx=int($picx*$zoom);
|
||||||
@ -475,9 +499,9 @@ sub FW_detailFn {
|
|||||||
my $mapx = $lonlo-$lonru;
|
my $mapx = $lonlo-$lonru;
|
||||||
my $mapy = $latlo-$latru;
|
my $mapy = $latlo-$latru;
|
||||||
|
|
||||||
AttrVal($name,'scaleToMeterXY', $hash->{helper}{scaleToMeterLongitude} . ' ' .$hash->{helper}{scaleToMeterLatitude}) =~ /(-?\d+)\s+(-?\d+)/;
|
my ( $scx, $scy ) = AttrVal($name,'scaleToMeterXY', $hash->{helper}{scaleToMeterLongitude} . ' ' .$hash->{helper}{scaleToMeterLatitude}) =~ /(-?\d+)\s+(-?\d+)/;
|
||||||
my $scalx = ( $lonru - $lonlo ) * $1;
|
my $scalx = ( $lonru - $lonlo ) * $scx;
|
||||||
my $scaly = ( $latlo - $latru ) * $2;
|
my $scaly = ( $latlo - $latru ) * $scy;
|
||||||
|
|
||||||
# CHARGING STATION POSITION
|
# CHARGING STATION POSITION
|
||||||
my $csimgpos = AttrVal( $name,"chargingStationImagePosition","right" );
|
my $csimgpos = AttrVal( $name,"chargingStationImagePosition","right" );
|
||||||
@ -494,10 +518,19 @@ sub FW_detailFn {
|
|||||||
my $limi = '';
|
my $limi = '';
|
||||||
if ($arealimits) {
|
if ($arealimits) {
|
||||||
my @lixy = (split(/\s|,|\R$/,$arealimits));
|
my @lixy = (split(/\s|,|\R$/,$arealimits));
|
||||||
|
my @liar = ();
|
||||||
$limi = int( ( $lonlo - $lixy[ 0 ] ) * $picx / $mapx ) . "," . int( ( $latlo - $lixy[ 1 ] ) * $picy / $mapy );
|
$limi = int( ( $lonlo - $lixy[ 0 ] ) * $picx / $mapx ) . "," . int( ( $latlo - $lixy[ 1 ] ) * $picy / $mapy );
|
||||||
for (my $i=2;$i<@lixy;$i+=2){
|
for (my $i=2;$i<@lixy;$i+=2){
|
||||||
$limi .= ",".int( ( $lonlo - $lixy[ $i ] ) * $picx / $mapx).",".int( ( $latlo-$lixy[$i+1] ) * $picy / $mapy);
|
$limi .= ",".int( ( $lonlo - $lixy[ $i ] ) * $picx / $mapx).",".int( ( $latlo - $lixy[$i+1] ) * $picy / $mapy);
|
||||||
|
my $x = ( $lonlo - $lixy[ $i ] ) * $scx;
|
||||||
|
my $y = ( $latlo - $lixy[$i+1] ) * $scy;
|
||||||
|
push( @liar, [ $x, $y ]);
|
||||||
}
|
}
|
||||||
|
my $x0 = ( $lonlo - $lixy[ 0 ] ) * $scx;
|
||||||
|
my $y0 = ( $latlo - $lixy[ 1] ) * $scy;
|
||||||
|
unshift( @liar, [ $x0, $y0 ]);
|
||||||
|
push( @liar, [ $x0, $y0 ]);
|
||||||
|
$hash->{helper}{statistics}{mowingArea} = int( abs( polygonArea( \@liar, 1, 1) ) );
|
||||||
}
|
}
|
||||||
$limi = 'data-areaLimitsPath="'.$limi.'"';
|
$limi = 'data-areaLimitsPath="'.$limi.'"';
|
||||||
|
|
||||||
@ -506,13 +539,33 @@ sub FW_detailFn {
|
|||||||
my $propli = '';
|
my $propli = '';
|
||||||
if ($propertylimits) {
|
if ($propertylimits) {
|
||||||
my @propxy = (split(/\s|,|\R$/,$propertylimits));
|
my @propxy = (split(/\s|,|\R$/,$propertylimits));
|
||||||
|
my @liar = ();
|
||||||
$propli = int(($lonlo-$propxy[0]) * $picx / $mapx).",".int(($latlo-$propxy[1]) * $picy / $mapy);
|
$propli = int(($lonlo-$propxy[0]) * $picx / $mapx).",".int(($latlo-$propxy[1]) * $picy / $mapy);
|
||||||
for (my $i=2;$i<@propxy;$i+=2){
|
for (my $i=2;$i<@propxy;$i+=2){
|
||||||
$propli .= ",".int(($lonlo-$propxy[$i]) * $picx / $mapx).",".int(($latlo-$propxy[$i+1]) * $picy / $mapy);
|
$propli .= ",".int(($lonlo-$propxy[$i]) * $picx / $mapx).",".int(($latlo-$propxy[$i+1]) * $picy / $mapy);
|
||||||
|
my $x = ( $lonlo - $propxy[ $i ] ) * $scx;
|
||||||
|
my $y = ( $latlo - $propxy[$i+1] ) * $scy;
|
||||||
|
push( @liar, [ $x, $y ]);
|
||||||
}
|
}
|
||||||
|
my $x0 = ( $lonlo - $propxy[ 0 ] ) * $scx;
|
||||||
|
my $y0 = ( $latlo - $propxy[ 1] ) * $scy;
|
||||||
|
unshift( @liar, [ $x0, $y0 ]);
|
||||||
|
push( @liar, [ $x0, $y0 ]);
|
||||||
|
$hash->{helper}{statistics}{propertyArea} = int( abs( polygonArea( \@liar, 1, 1) ) );
|
||||||
}
|
}
|
||||||
$propli = 'data-propertyLimitsPath="'.$propli.'"';
|
$propli = 'data-propertyLimitsPath="'.$propli.'"';
|
||||||
|
|
||||||
|
# MOWING AREA HULL
|
||||||
|
my $hulljson = AttrVal($name, 'mowingAreaHull', '[]');
|
||||||
|
my $hull = eval { decode_json( $hulljson ) };
|
||||||
|
if ( $@ ) {
|
||||||
|
Log3 $name, 1, "$type $name FW_detailFn: decode error: $@ \n $hulljson";
|
||||||
|
$hull = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash->{helper}{statistics}{hullArea} = int( polygonArea( $hull, $scalx/$picx, $scaly/$picy ) );
|
||||||
|
$hash->{helper}{mapupdate}{hullxy} = $hull;
|
||||||
|
|
||||||
my $ret = "";
|
my $ret = "";
|
||||||
$ret .= "<style>
|
$ret .= "<style>
|
||||||
.${type}_${name}_div{padding:0px !important;
|
.${type}_${name}_div{padding:0px !important;
|
||||||
@ -526,10 +579,13 @@ sub FW_detailFn {
|
|||||||
.${type}_${name}_canvas_1{
|
.${type}_${name}_canvas_1{
|
||||||
position: absolute; left: 0; top: 0; z-index: 1;}
|
position: absolute; left: 0; top: 0; z-index: 1;}
|
||||||
</style>";
|
</style>";
|
||||||
$ret .= "<div id='${type}_${name}_div' class='${type}_${name}_div' $mapDesign $csdata $limi $propli >";
|
$ret .= "<div id='${type}_${name}_div' class='${type}_${name}_div' $$mapDesign $csdata $limi $propli width='$picx' height='$picy' >";
|
||||||
$ret .= "<canvas id='${type}_${name}_canvas_0' class='${type}_${name}_canvas_0' width='$picx' height='$picy' ></canvas>";
|
$ret .= "<canvas id='${type}_${name}_canvas_0' class='${type}_${name}_canvas_0' width='$picx' height='$picy' ></canvas>";
|
||||||
$ret .= "<canvas id='${type}_${name}_canvas_1' class='${type}_${name}_canvas_1' width='$picx' height='$picy' ></canvas>";
|
$ret .= "<canvas id='${type}_${name}_canvas_1' class='${type}_${name}_canvas_1' width='$picx' height='$picy' ></canvas>";
|
||||||
$ret .= "</div>";
|
$ret .= "</div>";
|
||||||
|
$ret .= "<button title='Sends the hull polygon points to attribute mowingAreaHull.' onclick='AutomowerConnectGetHull( \"$FW_ME/$type/$name/json\" )'>mowingAreaHullToAttribute</button>"
|
||||||
|
if ( -e "$FW_dir/$hash->{helper}{FWEXTA}{path}/$hash->{helper}{FWEXTA}{file}" && !AttrVal( $name,'mowingAreaHull','' ) && $$mapDesign =~ m/hullCalculate="1"/g );
|
||||||
|
$ret .= "<br>";
|
||||||
$hash->{helper}{detailFnFirst} = 1;
|
$hash->{helper}{detailFnFirst} = 1;
|
||||||
my $mid = $hash->{helper}{map_init_delay};
|
my $mid = $hash->{helper}{map_init_delay};
|
||||||
InternalTimer( gettimeofday() + $mid, \&FW_detailFn_Update, $hash, 0 );
|
InternalTimer( gettimeofday() + $mid, \&FW_detailFn_Update, $hash, 0 );
|
||||||
@ -608,6 +664,7 @@ sub FW_detailFn_Update {
|
|||||||
$hash->{helper}{mapupdate}{picx} = $picx;
|
$hash->{helper}{mapupdate}{picx} = $picx;
|
||||||
$hash->{helper}{mapupdate}{picy} = $picy;
|
$hash->{helper}{mapupdate}{picy} = $picy;
|
||||||
$hash->{helper}{mapupdate}{scalx} = $scalx;
|
$hash->{helper}{mapupdate}{scalx} = $scalx;
|
||||||
|
$hash->{helper}{mapupdate}{scaly} = $scaly;
|
||||||
$hash->{helper}{mapupdate}{errdesc} = [ "$errdesc", "$errdate", "$errstate" ];
|
$hash->{helper}{mapupdate}{errdesc} = [ "$errdesc", "$errdate", "$errstate" ];
|
||||||
$hash->{helper}{mapupdate}{posxy} = \@posxy;
|
$hash->{helper}{mapupdate}{posxy} = \@posxy;
|
||||||
$hash->{helper}{mapupdate}{poserrxy} = \@poserrxy;
|
$hash->{helper}{mapupdate}{poserrxy} = \@poserrxy;
|
||||||
@ -1057,7 +1114,7 @@ sub getNewAccessToken {
|
|||||||
##############################################################
|
##############################################################
|
||||||
|
|
||||||
sub CMD {
|
sub CMD {
|
||||||
my ($hash,@cmd) = @_;
|
my ( $hash, @cmd ) = @_;
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
my $type = $hash->{TYPE};
|
my $type = $hash->{TYPE};
|
||||||
my $iam = "$type $name CMD:";
|
my $iam = "$type $name CMD:";
|
||||||
@ -1089,6 +1146,8 @@ my $header = "Accept: application/vnd.api+json\r\nX-Api-Key: ".$client_id."\r\nA
|
|||||||
elsif ($cmd[0] eq "Pause") { $json = '{"data": {"type":"'.$cmd[0].'"}}'; $post = 'actions' }
|
elsif ($cmd[0] eq "Pause") { $json = '{"data": {"type":"'.$cmd[0].'"}}'; $post = 'actions' }
|
||||||
elsif ($cmd[0] eq "Park") { $json = '{"data": {"type":"'.$cmd[0].'","attributes":{"duration":'.$cmd[1].'}}}'; $post = 'actions' }
|
elsif ($cmd[0] eq "Park") { $json = '{"data": {"type":"'.$cmd[0].'","attributes":{"duration":'.$cmd[1].'}}}'; $post = 'actions' }
|
||||||
elsif ($cmd[0] eq "Start") { $json = '{"data": {"type":"'.$cmd[0].'","attributes":{"duration":'.$cmd[1].'}}}'; $post = 'actions' }
|
elsif ($cmd[0] eq "Start") { $json = '{"data": {"type":"'.$cmd[0].'","attributes":{"duration":'.$cmd[1].'}}}'; $post = 'actions' }
|
||||||
|
elsif ($cmd[0] eq "cuttingHeightInWorkArea")
|
||||||
|
{ $json = '{"data": {"type":"workArea","id":"'.$cmd[1].'","attributes":{"cuttingHight":'.$cmd[2].'}}}'; $post = 'workAreas/'.$cmd[1]; $method = 'PATCH' }
|
||||||
elsif ($cmd[0] eq "StartInWorkArea" && $cmd[2])
|
elsif ($cmd[0] eq "StartInWorkArea" && $cmd[2])
|
||||||
{ $json = '{"data": {"type":"'.$cmd[0].'","attributes":{"workAreaId":'.$cmd[1].',"duration":'.$cmd[2].'}}}'; $post = 'actions' }
|
{ $json = '{"data": {"type":"'.$cmd[0].'","attributes":{"workAreaId":'.$cmd[1].',"duration":'.$cmd[2].'}}}'; $post = 'actions' }
|
||||||
elsif ($cmd[0] eq "StartInWorkArea" && !$cmd[2])
|
elsif ($cmd[0] eq "StartInWorkArea" && !$cmd[2])
|
||||||
@ -1185,7 +1244,7 @@ sub CMDResponse {
|
|||||||
|
|
||||||
readingsEndUpdate($hash, 1);
|
readingsEndUpdate($hash, 1);
|
||||||
|
|
||||||
Log3 $name, 2, "\n$iam \n\$statuscode >$statuscode<\n\$err >$err<,\n\$data >$data<\n\$param->url $param->{url}";
|
Log3 $name, 2, "\n$iam \n\$statuscode >$statuscode<\n\$err >$err<,\n\$data >$data<\n\$param->{url} >$param->{url}<\n\$param->{data} >$param->{data}<";
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1216,6 +1275,15 @@ sub Set {
|
|||||||
CommandAttr( $hash, "$name chargingStationCoordinates $xm $ym" );
|
CommandAttr( $hash, "$name chargingStationCoordinates $xm $ym" );
|
||||||
return undef;
|
return undef;
|
||||||
|
|
||||||
|
# } elsif ( $setName eq 'mowingAreaHullToAttribute' ) {
|
||||||
|
|
||||||
|
# if ( $FW_ME ) {
|
||||||
|
# map {
|
||||||
|
# ::FW_directNotify("#FHEMWEB:$_", "AutomowerConnectGetHull ( '$FW_ME/$type/$name/json' )","");
|
||||||
|
# } devspec2array("TYPE=FHEMWEB");
|
||||||
|
# return undef;
|
||||||
|
# }
|
||||||
|
|
||||||
} elsif ( $setName eq 'defaultDesignAttributesToAttribute' ) {
|
} elsif ( $setName eq 'defaultDesignAttributesToAttribute' ) {
|
||||||
|
|
||||||
my $design = $hash->{helper}{mapdesign};
|
my $design = $hash->{helper}{mapdesign};
|
||||||
@ -1293,7 +1361,7 @@ sub Set {
|
|||||||
CMD($hash,$setName);
|
CMD($hash,$setName);
|
||||||
return undef;
|
return undef;
|
||||||
|
|
||||||
} elsif ( ReadingsVal( $name, 'device_state', 'defined' ) !~ /defined|initialized|authentification|authenticated|update/ && $setName =~ /^(StartInWorkArea)$/ && AttrVal( $name, 'testing', '' ) ) {
|
} elsif ( ReadingsVal( $name, 'device_state', 'defined' ) !~ /defined|initialized|authentification|authenticated|update/ && $setName =~ /^(StartInWorkArea|cuttingHeightInWorkArea)$/ && AttrVal( $name, 'testing', '' ) ) {
|
||||||
|
|
||||||
my $id = undef;
|
my $id = undef;
|
||||||
$id = name2id( $hash, $setVal, 'workAreas' ) if ( $setVal !~ /^(\d+)$/ );
|
$id = name2id( $hash, $setVal, 'workAreas' ) if ( $setVal !~ /^(\d+)$/ );
|
||||||
@ -1325,7 +1393,7 @@ sub Set {
|
|||||||
my $ret = " getNewAccessToken:noArg ParkUntilFurtherNotice:noArg ParkUntilNextSchedule:noArg Pause:noArg Start:selectnumbers,60,60,600,0,lin Park:selectnumbers,60,60,600,0,lin ResumeSchedule:noArg getUpdate:noArg client_secret ";
|
my $ret = " getNewAccessToken:noArg ParkUntilFurtherNotice:noArg ParkUntilNextSchedule:noArg Pause:noArg Start:selectnumbers,60,60,600,0,lin Park:selectnumbers,60,60,600,0,lin ResumeSchedule:noArg getUpdate:noArg client_secret ";
|
||||||
$ret .= "chargingStationPositionToAttribute:noArg headlight:ALWAYS_OFF,ALWAYS_ON,EVENING_ONLY,EVENING_AND_NIGHT cuttingHeight:1,2,3,4,5,6,7,8,9 mowerScheduleToAttribute:noArg ";
|
$ret .= "chargingStationPositionToAttribute:noArg headlight:ALWAYS_OFF,ALWAYS_ON,EVENING_ONLY,EVENING_AND_NIGHT cuttingHeight:1,2,3,4,5,6,7,8,9 mowerScheduleToAttribute:noArg ";
|
||||||
$ret .= "sendScheduleFromAttributeToMower:noArg defaultDesignAttributesToAttribute:noArg mapZonesTemplateToAttribute:noArg ";
|
$ret .= "sendScheduleFromAttributeToMower:noArg defaultDesignAttributesToAttribute:noArg mapZonesTemplateToAttribute:noArg ";
|
||||||
$ret .= "StartInWorkArea " if ( $hash->{helper}{mower}{attributes}{capabilities}{workAreas} && AttrVal( $name, 'testing', '' ) );
|
$ret .= "StartInWorkArea cuttingHeightInWorkArea " if ( $hash->{helper}{mower}{attributes}{capabilities}{workAreas} && AttrVal( $name, 'testing', '' ) );
|
||||||
$ret .= "confirmError:noArg " if ( AttrVal( $name, 'testing', '' ) );
|
$ret .= "confirmError:noArg " if ( AttrVal( $name, 'testing', '' ) );
|
||||||
$ret .= "stayOutZone_enable stayOutZone_disable " if ( $hash->{helper}{mower}{attributes}{capabilities}{stayOutZones} && AttrVal( $name, 'testing', '' ) );
|
$ret .= "stayOutZone_enable stayOutZone_disable " if ( $hash->{helper}{mower}{attributes}{capabilities}{stayOutZones} && AttrVal( $name, 'testing', '' ) );
|
||||||
return "Unknown argument $setName, choose one of".$ret;
|
return "Unknown argument $setName, choose one of".$ret;
|
||||||
@ -1391,6 +1459,20 @@ sub Attr {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
##########
|
||||||
|
} elsif( $attrName eq "mowingAreaHull" ) {
|
||||||
|
|
||||||
|
if( $cmd eq "set" ) {
|
||||||
|
|
||||||
|
my $perl = eval { decode_json ( $attrVal ) };
|
||||||
|
|
||||||
|
if ($@) {
|
||||||
|
return "$iam $cmd $attrName decode error: $@ \n $attrVal";
|
||||||
|
}
|
||||||
|
Log3 $name, 4, "$iam $cmd $attrName";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
##########
|
##########
|
||||||
} elsif( $attrName eq "weekdaysToResetWayPoints" ) {
|
} elsif( $attrName eq "weekdaysToResetWayPoints" ) {
|
||||||
|
|
||||||
@ -2398,6 +2480,14 @@ sub listStatisticsData {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my @fences = qw(hull mowing property);
|
||||||
|
|
||||||
|
for my $item ( @fences ) {
|
||||||
|
|
||||||
|
$ret .= '<tr class="column '.( $cnt++ % 2 ? 'odd' : 'even' ).'"><td> <b> calculated '.$item.' area </b>  </td><td> ' . $hash->{helper}{statistics}{$item.'Area'} . ' </td><td> qm </td></tr>' if ( $hash->{helper}{statistics}{$item.'Area'} );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$ret .= '</tbody></table>';
|
$ret .= '</tbody></table>';
|
||||||
$ret .= '<p><sup>1</sup> totalDriveDistance = totalRunningTime * '. sprintf( "%.2f", $hash->{helper}{mower}{attributes}{statistics}{totalDriveDistance} / $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} ) if ( $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} );
|
$ret .= '<p><sup>1</sup> totalDriveDistance = totalRunningTime * '. sprintf( "%.2f", $hash->{helper}{mower}{attributes}{statistics}{totalDriveDistance} / $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} ) if ( $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} );
|
||||||
$ret .= '<p><sup>2</sup> totalRunningTime = totalCuttingTime + totalSearchingTime';
|
$ret .= '<p><sup>2</sup> totalRunningTime = totalCuttingTime + totalSearchingTime';
|
||||||
@ -2541,7 +2631,7 @@ sub listInternalData {
|
|||||||
|
|
||||||
$ret .= '</tbody></table><p>';
|
$ret .= '</tbody></table><p>';
|
||||||
$ret .= '<table class="block wide">';
|
$ret .= '<table class="block wide">';
|
||||||
$ret .= '<caption><b>Way Point Stacks</b></caption><tbody>';
|
$ret .= '<caption><b>Way Point Stacks</b></caption><tbody>';
|
||||||
|
|
||||||
$ret .= '<tr class="col_header"><td> Used For Activities </td><td> Stack Name </td><td> Current Size </td><td> Max Size </td></tr>';
|
$ret .= '<tr class="col_header"><td> Used For Activities </td><td> Stack Name </td><td> Current Size </td><td> Max Size </td></tr>';
|
||||||
$ret .= '<tr class="column odd"><td>PARKED_IN_CS, CHARGING </td><td> cspos </td><td> ' . $csnr . ' </td><td> ' . $csnrmax . ' </td></tr>';
|
$ret .= '<tr class="column odd"><td>PARKED_IN_CS, CHARGING </td><td> cspos </td><td> ' . $csnr . ' </td><td> ' . $csnrmax . ' </td></tr>';
|
||||||
@ -2549,27 +2639,37 @@ sub listInternalData {
|
|||||||
$ret .= '<tr class="column odd"><td>NOT_APPLICABLE with error time stamp </td><td> lasterror/positions </td><td> ' . $ernr . ' </td><td> - </td></tr>';
|
$ret .= '<tr class="column odd"><td>NOT_APPLICABLE with error time stamp </td><td> lasterror/positions </td><td> ' . $ernr . ' </td><td> - </td></tr>';
|
||||||
|
|
||||||
$ret .= '</tbody></table>';
|
$ret .= '</tbody></table>';
|
||||||
if ( $hash->{TYPE} eq 'AutomowerConnect' ) {
|
$ret .= '<p><table class="block wide">';
|
||||||
|
$ret .= '<caption><b>Rest API Data</b></caption><tbody>';
|
||||||
|
|
||||||
$ret .= '<p><table class="block wide">';
|
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Link to APIs</td><td><a target="_blank" href="https://developer.husqvarnagroup.cloud/">Husqvarna Developer</a></td></tr>';
|
||||||
$ret .= '<caption><b>Rest API Data</b></caption><tbody>';
|
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Authentification API URL</td><td>' . AUTHURL . '</td></tr>';
|
||||||
|
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Automower Connect API URL</td><td>' . APIURL . '</td></tr>';
|
||||||
|
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Websocket IO Device name</td><td>' . WSDEVICENAME . '</td></tr>';
|
||||||
|
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Client-Id</td><td>' . $hash->{helper}{client_id} . '</td></tr>';
|
||||||
|
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Grant-Type</td><td>' . $hash->{helper}{grant_type} . '</td></tr>';
|
||||||
|
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> User-Id</td><td>' . ReadingsVal($name, '.user_id', '-') . '</td></tr>';
|
||||||
|
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Provider</td><td>' . ReadingsVal($name, '.provider', '-') . '</td></tr>';
|
||||||
|
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Scope</td><td>' . ReadingsVal($name, '.scope', '-') . '</td></tr>';
|
||||||
|
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Token Type</td><td>' . ReadingsVal($name, '.token_type', '-') . '</td></tr>';
|
||||||
|
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Token Expires</td><td> ' . FmtDateTime( ReadingsVal($name, '.expires', '0') ) . '</td></tr>';
|
||||||
|
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Access Token</td><td style="word-wrap:break-word; max-width:40em">' . ReadingsVal($name, '.access_token', '0') . '</td></tr>';
|
||||||
|
|
||||||
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Link to APIs</td><td><a target="_blank" href="https://developer.husqvarnagroup.cloud/">Husqvarna Developer</a></td></tr>';
|
$ret .= '</tbody></table>';
|
||||||
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Authentification API URL</td><td>' . AUTHURL . '</td></tr>';
|
$ret .= '<p><table class="block wide">';
|
||||||
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Automower Connect API URL</td><td>' . APIURL . '</td></tr>';
|
$ret .= '<caption><b>Default mapDesignAttributes</b></caption><tbody>';
|
||||||
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Websocket IO Device name</td><td>' . WSDEVICENAME . '</td></tr>';
|
|
||||||
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Client-Id</td><td>' . $hash->{helper}{client_id} . '</td></tr>';
|
|
||||||
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Grant-Type</td><td>' . $hash->{helper}{grant_type} . '</td></tr>';
|
|
||||||
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> User-Id</td><td>' . ReadingsVal($name, '.user_id', '-') . '</td></tr>';
|
|
||||||
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Provider</td><td>' . ReadingsVal($name, '.provider', '-') . '</td></tr>';
|
|
||||||
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Scope</td><td>' . ReadingsVal($name, '.scope', '-') . '</td></tr>';
|
|
||||||
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Token Type</td><td>' . ReadingsVal($name, '.token_type', '-') . '</td></tr>';
|
|
||||||
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Token Expires</td><td> ' . FmtDateTime( ReadingsVal($name, '.expires', '0') ) . '</td></tr>';
|
|
||||||
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td> Access Token</td><td style="word-wrap:break-word; max-width:40em">' . ReadingsVal($name, '.access_token', '0') . '</td></tr>';
|
|
||||||
|
|
||||||
$ret .= '</tbody></table>';
|
my $mapdesign = $hash->{helper}{mapdesign};
|
||||||
|
$mapdesign =~ s/\n/<br>/g;
|
||||||
|
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td style="word-wrap:break-word; max-width:40em">' . $mapdesign . '</td></tr>';
|
||||||
|
|
||||||
}
|
$ret .= '</tbody></table>';
|
||||||
|
$ret .= '<p><table class="block wide">';
|
||||||
|
$ret .= '<caption><b>Third Party Software</b></caption><tbody>';
|
||||||
|
|
||||||
|
$ret .= '<tr class="column ' . ( $cnt++ % 2 ? "odd" : "even" ) . '"><td>hull calculation (hull.js)</td><td style="word-wrap:break-word; max-width:40em"> Server: ' . $hash->{helper}{FWEXTA}{url} . '</td></tr>';
|
||||||
|
|
||||||
|
$ret .= '</tbody></table>';
|
||||||
|
|
||||||
$ret .= '</html>';
|
$ret .= '</html>';
|
||||||
return $ret;
|
return $ret;
|
||||||
@ -2624,6 +2724,66 @@ sub FmtDateTimeGMT {
|
|||||||
my $ret = POSIX::strftime( "%F %H:%M:%S", gmtime( $ti ) );
|
my $ret = POSIX::strftime( "%F %H:%M:%S", gmtime( $ti ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#########################
|
||||||
|
sub polygonArea {
|
||||||
|
my ( $ptsref, $sx, $sy ) = @_;
|
||||||
|
my $sumarea = 0;
|
||||||
|
my @pts = @{$ptsref};
|
||||||
|
|
||||||
|
for (my $i = 0; $i < @pts; $i++) {
|
||||||
|
my $addX = $pts[$i][0]*$sx;
|
||||||
|
my $addY = $pts[$i == @pts - 1 ? 0 : $i + 1][1]*$sy;
|
||||||
|
my $subX = $pts[$i == @pts - 1 ? 0 : $i + 1][0]*$sx;
|
||||||
|
my $subY = $pts[$i][1]*$sy;
|
||||||
|
$sumarea += ($addX * $addY * 0.5);
|
||||||
|
$sumarea -= ($subX * $subY * 0.5);
|
||||||
|
|
||||||
|
}
|
||||||
|
return $sumarea;
|
||||||
|
}
|
||||||
|
|
||||||
|
#########################
|
||||||
|
sub getTpFile {
|
||||||
|
my ( $hash, $url, $path, $file ) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
my $msg = ::GetFileFromURL( $url );
|
||||||
|
if ( $msg ) {
|
||||||
|
my $fh;
|
||||||
|
|
||||||
|
if( !open( $fh, ">", "$path/$file" ) ) {
|
||||||
|
|
||||||
|
Log3 $name, 1, "$name getTpFile: Can't open $path/$file";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
print $fh $msg;
|
||||||
|
close( $fh );
|
||||||
|
readingsSingleUpdate( $hash, 'third_party_library', "$file downloaded to: $path", 1 );
|
||||||
|
Log3 $name, 1, "$name getTpFile: third party library downloaded from $url to $path";
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
#########################
|
||||||
|
sub getDesignAttr {
|
||||||
|
my ( $hash ) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
my @designDefault = split( /\R/,$hash->{helper}{mapdesign} );
|
||||||
|
my @designAttr = split( /\R/, AttrVal( $name, 'mapDesignAttributes', '' ) );
|
||||||
|
my $hsh = '';
|
||||||
|
my $val = '';
|
||||||
|
my %desDef = map { ( $hsh, $val ) = $_ =~ /(.*)=(.*)/; $hsh => $val } @designDefault;
|
||||||
|
%desDef = map { ( $hsh, $val ) = $_ =~ /(.*)=(.*)/; $hsh => $val } @designAttr;
|
||||||
|
my $desDef = \%desDef;
|
||||||
|
my @mergedDesign = map { "$_=$desDef->{$_}" } sort keys %desDef;
|
||||||
|
my $design = 'data-' . join( 'data-', @mergedDesign );
|
||||||
|
return \$design;
|
||||||
|
}
|
||||||
|
|
||||||
##############################################################
|
##############################################################
|
||||||
#
|
#
|
||||||
# WEBSOCKET
|
# WEBSOCKET
|
||||||
|
@ -44,6 +44,38 @@ function AutomowerConnectShowError( ctx, div, dev, picx, picy, errdesc, erray )
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function AutomowerConnectHull( ctx, div, pos, type ) {
|
||||||
|
// log("array length: "+pos.length);
|
||||||
|
if ( pos.length > 3 ) {
|
||||||
|
// draw limits
|
||||||
|
ctx.beginPath();
|
||||||
|
|
||||||
|
ctx.lineWidth = div.getAttribute( 'data-'+ type + 'LineWidth' );
|
||||||
|
ctx.strokeStyle = div.getAttribute( 'data-'+ type + 'Color' );
|
||||||
|
ctx.setLineDash( [] );
|
||||||
|
|
||||||
|
for (var i=0;i < pos.length; i++ ) {
|
||||||
|
ctx.lineTo( pos[i][0], pos[i][1]);
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// hull connector
|
||||||
|
if ( div.getAttribute( 'data-'+ type + 'Connector' ) ) {
|
||||||
|
for ( var i = 0; i < pos.length; i++ ) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.setLineDash( [] );
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.strokeStyle = div.getAttribute( 'data-'+ type + 'Color' );
|
||||||
|
ctx.fillStyle= 'white';
|
||||||
|
ctx.moveTo( pos[i][0], pos[i][1]);
|
||||||
|
ctx.arc( pos[i][0], pos[i][1], 2, 0, 2 * Math.PI, false);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function AutomowerConnectLimits( ctx, div, pos, type ) {
|
function AutomowerConnectLimits( ctx, div, pos, type ) {
|
||||||
// log("array length: "+pos.length);
|
// log("array length: "+pos.length);
|
||||||
if ( pos.length > 3 ) {
|
if ( pos.length > 3 ) {
|
||||||
@ -315,7 +347,7 @@ function AutomowerConnectUpdateJson ( path ) {
|
|||||||
$.getJSON( path, function( data, textStatus ) {
|
$.getJSON( path, function( data, textStatus ) {
|
||||||
console.log( 'AutomowerConnectUpdateJson ( \''+path+'\' ): status '+textStatus );
|
console.log( 'AutomowerConnectUpdateJson ( \''+path+'\' ): status '+textStatus );
|
||||||
if ( textStatus == 'success')
|
if ( textStatus == 'success')
|
||||||
AutomowerConnectUpdateDetail ( data.name, data.type, data.detailfnfirst, data.picx, data.picy, data.scalx, data.errdesc, data.posxy, data.poserrxy );
|
AutomowerConnectUpdateDetail ( data.name, data.type, data.detailfnfirst, data.picx, data.picy, data.scalx, data.scaly, data.errdesc, data.posxy, data.poserrxy, data.hullxy );
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -325,14 +357,48 @@ function AutomowerConnectUpdateJsonFtui ( path ) {
|
|||||||
$.getJSON( path, function( data, textStatus ) {
|
$.getJSON( path, function( data, textStatus ) {
|
||||||
console.log( 'AutomowerConnectUpdateJsonFtui ( \''+path+'\' ): status '+textStatus );
|
console.log( 'AutomowerConnectUpdateJsonFtui ( \''+path+'\' ): status '+textStatus );
|
||||||
if ( textStatus == 'success')
|
if ( textStatus == 'success')
|
||||||
AutomowerConnectUpdateDetail ( data.name, data.type, 1, data.picx, data.picy, data.scalx, data.errdesc, data.posxy, data.poserrxy );
|
AutomowerConnectUpdateDetail ( data.name, data.type, 1, data.picx, data.picy, data.scalx, data.scaly, data.errdesc, data.posxy, data.poserrxy, data.hullxy );
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//AutomowerConnectUpdateDetail (<devicename>, <type>, <detailfnfirst>, <imagesize x>, <imagesize y>,<scale x>, <error description>, <path array>, <error array>)
|
function AutomowerConnectGetHull ( path ) {
|
||||||
function AutomowerConnectUpdateDetail (dev, type, detailfnfirst, picx, picy, scalx, errdesc, pos, erray) {
|
$.getJSON( path, function( data, textStatus ) {
|
||||||
|
console.log( 'AutomowerConnectGetHull ( \''+path+'\' ): status '+textStatus );
|
||||||
|
|
||||||
|
if ( textStatus == 'success') {
|
||||||
|
// data.name, data.type, data.picx, data.picy, data.scalx, data.scaly, data.errdesc, data.posxy, data.poserrxy );
|
||||||
|
const div = document.getElementById(data.type+'_'+data.name+'_div');
|
||||||
|
const pos =data.posxy;
|
||||||
|
|
||||||
|
if ( div && div.getAttribute( 'data-hullCalculate' ) && typeof hull === "function" ){
|
||||||
|
const wypts = [];
|
||||||
|
|
||||||
|
for ( let i = 0; i < pos.length; i+=3 ){
|
||||||
|
|
||||||
|
if ( pos[i+2] == "M") wypts.push( [ pos[i], pos[i+1] ] );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( wypts.length > 50 ) {
|
||||||
|
|
||||||
|
const wyres = div.getAttribute( 'data-hullResolution' );
|
||||||
|
const hullpts = hull( wypts, wyres );
|
||||||
|
FW_cmd( FW_root+"?cmd=attr "+data.name+" mowingAreaHull "+JSON.stringify( hullpts )+"&XHR=1",function(data){setTimeout(()=>{window.location.reload()},500)} );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//AutomowerConnectUpdateDetail (<devicename>, <type>, <detailfnfirst>, <imagesize x>, <imagesize y>, <scale x>, <scale y>, <error description>, <path array>, <error array>, <hull array>)
|
||||||
|
function AutomowerConnectUpdateDetail (dev, type, detailfnfirst, picx, picy, scalx, scaly, errdesc, pos, erray, hullxy) {
|
||||||
const colorat = {
|
const colorat = {
|
||||||
"U" : "otherActivityPath",
|
"U" : "otherActivityPath",
|
||||||
"N" : "errorPath",
|
"N" : "errorPath",
|
||||||
@ -369,6 +435,30 @@ function AutomowerConnectUpdateDetail (dev, type, detailfnfirst, picx, picy, sca
|
|||||||
const plixy = div.getAttribute( 'data-propertyLimitsPath' ).split( "," );
|
const plixy = div.getAttribute( 'data-propertyLimitsPath' ).split( "," );
|
||||||
if ( plixy.length > 0 ) AutomowerConnectLimits( ctx0, div, plixy, 'property' );
|
if ( plixy.length > 0 ) AutomowerConnectLimits( ctx0, div, plixy, 'property' );
|
||||||
|
|
||||||
|
// draw hull
|
||||||
|
if ( div.getAttribute( 'data-hullCalculate' ) && typeof hull === "function" && hullxy.length == 0 ) {
|
||||||
|
const pts = [];
|
||||||
|
|
||||||
|
for ( let i = 0; i < pos.length; i+=3 ){
|
||||||
|
|
||||||
|
if ( pos[i+2] == "M") pts.push( [ pos[i], pos[i+1] ] );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( pts.length > 50 ) {
|
||||||
|
|
||||||
|
const res = div.getAttribute( 'data-hullResolution' );
|
||||||
|
const hullpts = hull( pts, res );
|
||||||
|
AutomowerConnectHull( ctx0, div, hullpts, 'hull' );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if ( hullxy.length > 0 ) {
|
||||||
|
|
||||||
|
AutomowerConnectHull( ctx0, div, hullxy, 'hull' );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// draw scale
|
// draw scale
|
||||||
AutomowerConnectScale( ctx0, picx, picy, scalx );
|
AutomowerConnectScale( ctx0, picx, picy, scalx );
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user