Committed v2.3.0

This commit is contained in:
MickMake 2022-10-08 12:07:03 +11:00
parent 0daafb1d6d
commit d24e69975d
30 changed files with 739 additions and 4170 deletions

1127
.idea/workspace.xml generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,341 @@
# Examples of all known working commands
## Authentication
GoSungrow api login
## High-level info commands
```
Usage:
GoSungrow info [command]
Available Commands:
get Info - Get info from iSolarCloud (table)
raw Info - Get info from iSolarCloud (raw)
json Info - Get info from iSolarCloud (json)
csv Info - Get info from iSolarCloud (json)
put Info - Set info on iSolarCloud
```
```
Usage:
GoSungrow info get [command]
Available Commands:
point-names Info - Get iSolarCloud point names.
mqtt Info - Get iSolarCloud MQTT service login details.
search-point-names Info - Get iSolarCloud search point names.
devices Info - Get iSolarCloud devices.
models Info - Get ALL iSolarCloud models.
templates Info - Get all defined templates.
template-points Info - List data points used in report template.
device-points Info - List all available device data points.
```
## Get device details
GoSungrow info get device
GoSungrow info get search-point-names
GoSungrow info get models
## Templates
```
GoSungrow info get templates
┏━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓
┃ Template Id ┃ Template Name ┃ Update On ┃
┣━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━┫
┃ 8042 │ Critical │ 2022-02-15 13:00:28 ┃
┃ 8041 │ extras │ 2022-02-15 09:40:04 ┃
┃ 8036 │ C │ 2022-02-15 09:31:35 ┃
┃ 8039 │ v │ 2022-02-15 09:31:10 ┃
┃ 8040 │ A │ 2022-02-15 09:30:56 ┃
┃ 8034 │ Percent │ 2022-02-15 09:30:41 ┃
┃ 8038 │ MWh │ 2022-02-15 09:09:22 ┃
┃ 8037 │ MW │ 2022-02-15 09:03:22 ┃
┃ 8033 │ kW │ 2022-02-15 09:01:19 ┃
┃ 8035 │ Hours │ 2022-02-15 08:55:56 ┃
┃ 8031 │ kWh │ 2022-02-15 07:57:36 ┃
┃ 7981 │ Power │ 2022-02-09 10:03:40 ┃
┗━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━┛
```
## High-level data commands
```
Usage:
GoSungrow data [command]
Available Commands:
get Data - Get high-level data from iSolarCloud (table)
raw Data - Get high-level data from iSolarCloud (raw)
json Data - Get high-level data from iSolarCloud (json)
csv Data - Get high-level data from iSolarCloud (json)
graph Data - Get high-level data from iSolarCloud (graph)
```
```
Usage:
GoSungrow data get [command]
Available Commands:
stats Data - Get current inverter stats, (last 5 minutes).
template Data - Get data from report template.
points Data - Get points data for a specific date.
real-time Data - Get iSolarCloud real-time data.
psdetails Data - Get iSolarCloud ps details.
```
## General
```
GoSungrow data get stats
┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━┓
┃ Date ┃ Point Id ┃ Point Name ┃ Value ┃ Unit ┃
┣━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━┫
┃ 2022-10-08 09:55:00 │ 1129147.0 │ Co2 Reduce │ 0 │ kg ┃
┃ 2022-10-08 09:55:00 │ 1129147.0 │ Co2 Reduce Total │ 5819 │ kg ┃
┃ 2022-10-08 09:55:00 │ 1129147.0 │ Curr Power │ 788 │ W ┃
┃ 2022-10-08 09:55:00 │ 1129147.0 │ Daily Irradiation │ -- │ ┃
┃ 2022-10-08 09:55:00 │ 1129147.0 │ Equivalent Hour │ 0.27 │ Hour ┃
...
┃ 2022-10-08 09:55:00 │ 1171348.0 │ Today Income │ 0.397 │ AUD ┃
┃ 2022-10-08 09:55:00 │ 1171348.0 │ Total Capacity │ │ ┃
┃ 2022-10-08 09:55:00 │ 1171348.0 │ Total Energy │ 176.5 │ kWh ┃
┃ 2022-10-08 09:55:00 │ 1171348.0 │ Total Income │ 35.806 │ AUD ┃
┃ 2022-10-08 09:55:00 │ 1171348.0 │ Use Energy │ 6.7 │ kWh ┃
┗━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━┷━━━━━━┛
```
## Templates
```
GoSungrow info get template-points 7981
┏━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━┓
┃ PointStruct Id ┃ Description ┃ Unit ┃
┣━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━┫
┃ 1129147_11_0_0.p83032 │ Meter AC Power │ kW ┃
┃ 1129147_11_0_0.p83119 │ Daily Feed-in Energy (PV) │ kWh ┃
┃ 1129147_11_0_0.p83549 │ Grid active power │ kW ┃
┃ 1129147_11_0_0.p83002 │ Inverter AC Power │ kW ┃
┃ 1129147_11_0_0.p83011 │ Meter E-daily Consumption │ kWh ┃
┃ 1129147_11_0_0.p83022 │ Daily Yield of Plant │ kWh ┃
┃ 1129147_11_0_0.p83006 │ Meter Daily Yield │ kWh ┃
┃ 1129147_11_0_0.p83033 │ Plant Power │ kW ┃
┃ 1129147_11_0_0.p83072 │ Daily Feed-in Energy │ kWh ┃
┃ 1129147_11_0_0.p83097 │ Daily Load Energy Consumption from PV │ kWh ┃
┃ 1129147_11_0_0.p83102 │ Daily Purchased Energy │ kWh ┃
┃ 1129147_14_1_1.p13028 │ Daily Battery Charging Energy │ kWh ┃
┃ 1129147_14_1_1.p13112 │ Daily PV Yield │ kWh ┃
┃ 1129147_14_1_1.p13147 │ Daily Purchased Energy │ kWh ┃
┃ 1129147_14_1_1.p13173 │ Daily Feed-in Energy (PV) │ kWh ┃
┃ 1129147_14_1_1.p13174 │ Daily Battery Charging Energy from PV │ kWh ┃
┃ 1129147_14_1_1.p13199 │ Daily Load Energy Consumption │ kWh ┃
┃ 1129147_14_1_1.p13116 │ Daily Load Energy Consumption from PV │ kWh ┃
┃ 1129147_14_1_1.p13122 │ Daily Feed-in Energy │ kWh ┃
┗━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━┛
```
```
GoSungrow data get template 8042 20220212
┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━┓
┃ Date/Time ┃ Point Id ┃ Point Name ┃ Value ┃ Units ┃
┣━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━┫
┃ 2022-02-12 00:00:00 │ 1129147_11_0_0.p83106 │ Load Power │ 396 │ kW ┃
┃ 2022-02-12 00:05:00 │ 1129147_11_0_0.p83106 │ Load Power │ 480 │ kW ┃
┃ 2022-02-12 00:10:00 │ 1129147_11_0_0.p83106 │ Load Power │ 487 │ kW ┃
┃ 2022-02-12 00:15:00 │ 1129147_11_0_0.p83106 │ Load Power │ 497 │ kW ┃
┃ 2022-02-12 00:20:00 │ 1129147_11_0_0.p83106 │ Load Power │ 524 │ kW ┃
┃ 2022-02-12 00:25:00 │ 1129147_11_0_0.p83106 │ Load Power │ 541 │ kW ┃
┃ 2022-02-12 00:30:00 │ 1129147_11_0_0.p83106 │ Load Power │ 554 │ kW ┃
...
┃ 2022-02-12 23:55:00 │ 1129147_14_1_1.p13019 │ Internal Air Temperature │ 19.4 │ ℃ ┃
┃ 2022-02-13 00:00:00 │ 1129147_14_1_1.p13019 │ Internal Air Temperature │ 22.3 │ ℃ ┃
┗━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━┷━━━━━━━┛
```
GoSungrow data graph template 8042 20220212 '{"search_string":"p13019","min_left_axis":-6000,"max_left_axis":12000}'
GoSungrow data graph template 8042 20220212 '{"search_string":"p13019"}'
GoSungrow data graph template 8042 20220212 '{"search_string":"p83106","min_left_axis":-6000,"max_left_axis":12000}'
GoSungrow data graph template 8042 20220212 '{"search_string":"p83106"}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":"1","value_column":"4","search_column":"3","search_string":"p83106","file_name":"foo.png"}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":1,"value_column":4,"search_column":2,"search_string":"p13019","min_left_axis":0,"max_left_axis":0}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":1,"value_column":4,"search_column":2,"search_string":"p13149","min_left_axis":0,"max_left_axis":0}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":1,"value_column":4,"search_column":2,"search_string":"p83106","file_name":"foo.png"}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":1,"value_column":4,"search_column":2,"search_string":"p83106","min_left_axis":-6000,"max_left_axis":1000}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":1,"value_column":4,"search_column":2,"search_string":"p83106","min_left_axis":-6000,"max_left_axis":12000}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":1,"value_column":4,"search_column":2,"search_string":"p83106","min_left_axis":-6000,"max_left_axis":42000}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":1,"value_column":4,"search_column":2,"search_string":"p83106","min_left_axis":0,"max_left_axis":0}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":1,"value_column":4,"search_column":3,"search_string":"p83106","file_name":"foo.png"}'
GoSungrow data raw template '' 20220201
GoSungrow data save template '' 20220201
GoSungrow data save template 8042 20220212
## Hacking
## Base API commands
```
Usage:
GoSungrow api [command]
Examples:
GoSungrow api login
GoSungrow api get <endpoint> <args>
GoSungrow api raw <endpoint> <args>
GoSungrow api save <endpoint> <args>
GoSungrow api put <endpoint> <args>
Available Commands:
ls List iSolarCloud api endpoints/areas
login Login to iSolarCloud
get Get details from iSolarCloud
raw Raw details from iSolarCloud
save Save details from iSolarCloud as JSON
put Put details onto iSolarCloud
```
## Get device details
GoSungrow api get getPsList
GoSungrow api get getPsListByName
GoSungrow api get getPsDataSupplementTaskList
GoSungrow api get getPsUser
GoSungrow api get queryPsIdList
GoSungrow api get getInstallInfoList
GoSungrow api get getPsListStaticData
GoSungrow api get queryAllPsIdAndName
GoSungrow api get getPsDetail '{"ps_id":"1171348"}'
GoSungrow api get getPsDetailWithPsType '{"ps_id":"1171348"}'
GoSungrow api get getPsHealthState '{"ps_id":"1171348"}'
GoSungrow api get getPsWeatherList '{"ps_id":"1171348"}'
GoSungrow api get queryDeviceList '{"ps_id":"1171348"}'
GoSungrow api get queryDeviceListByUserId '{"ps_id":"1171348"}'
GoSungrow api get queryDeviceListForApp '{"ps_id":"1171348"}'
GoSungrow api get findPsType '{"ps_id":"1171348"}'
GoSungrow api get getDeviceList '{"ps_id":"1171348"}'
GoSungrow api get getIncomeSettingInfos '{"ps_id":"1171348"}'
GoSungrow api get getPowerChargeSettingInfo '{"ps_id":"1171348"}'
GoSungrow api get getPowerStationInfo '{"ps_id":"1171348"}'
GoSungrow api get WebAppService.getDeviceUuid '{"ps_key":"1171348"}'
GoSungrow api get getPowerStationForHousehold '{"ps_id":"1171348"}'
## Get device data
GoSungrow api get energyTrend
GoSungrow api get getKpiInfo
GoSungrow api get queryPsProfit '{"date_id":"20221001","date_type":"1","ps_id":"1171348"}'
GoSungrow api get getPowerStationData '{"date_id":"20221001","date_type":"1","ps_id":"1171348"}'
GoSungrow api get getHouseholdStoragePsReport '{"date_id":"20221001","date_type":"1","ps_id":"1171348"}'
GoSungrow api get queryPsProfit '{"date_id":"202210","date_type":"2","ps_id":"1171348"}'
GoSungrow api get getPowerStationData '{"date_id":"202210","date_type":"2","ps_id":"1171348"}'
GoSungrow api get getHouseholdStoragePsReport '{"date_id":"202210","date_type":"2","ps_id":"1171348"}'
GoSungrow api get queryPsProfit '{"date_id":"2022","date_type":"3","ps_id":"1171348"}'
GoSungrow api get getPowerStationData '{"date_id":"2022","date_type":"3","ps_id":"1171348"}'
GoSungrow api get getHouseholdStoragePsReport '{"date_id":"2022","date_type":"3","ps_id":"1171348"}'
GoSungrow api get getPowerTrendDayData '{"BeginTime":"20221004"}'
GoSungrow api get getPowerStatistics '{"ps_id":"1171348"}'
GoSungrow api get WebAppService.showPSView '{"ps_id":"1171348"}'
## Reports
GoSungrow api get getPsReport '{"report_type":"1","date_id":"20220201","date_type":"1","ps_id":"1171348"}'
GoSungrow api get getPsReport '{"report_type":"1","date_id":"20220201","date_type":"2","ps_id":"1171348"}'
GoSungrow api get getPsReport '{"report_type":"1","date_id":"20220201","date_type":"3","ps_id":"1171348"}'
GoSungrow api get reportList '{"ps_id":"1171348","report_type":"1"}'
GoSungrow api get reportList '{"ps_id":"1171348","report_type":"2"}'
GoSungrow api get reportList '{"ps_id":"1171348","report_type":"3"}'
GoSungrow api get reportList '{"ps_id":"1171348","report_type":"4"}'
## Templates
GoSungrow api get template 20220202
GoSungrow api get template 8042 20220212
GoSungrow api get getTemplateList
GoSungrow api get WebAppService.queryUserCurveTemplateData '{"template_id":"8042","date_type":"1","start_time":"20220223000000","end_time":"20220223235900"}'
## User/installer/support info
GoSungrow api get getUserList
GoSungrow api get getUserPsOrderList
GoSungrow api get getPhotoInfo
GoSungrow api get getOrgListByName
GoSungrow api get getOrgListByUserId
GoSungrow api get getOrgListForUser
GoSungrow api get queryUserList
GoSungrow api get getInstallerInfoByDealerOrgCodeOrId '{"dealer_org_code":"AUSCEKK7"}'
GoSungrow api get getInstallerInfoByDealerOrgCodeOrId '{"org_id":"362245"}'
GoSungrow api get getInstallerInfoByDealerOrgCodeOrId '{"org_id":"80384"}'
GoSungrow api get getInstallerInfoByDealerOrgCodeOrId '{"org_id":"80393"}'
GoSungrow api get getInstallerInfoByDealerOrgCodeOrId '{"org_id":"300977"}'
## Meta-data
GoSungrow api get getDeviceTypeInfoList
GoSungrow api get getDeviceTypeList
GoSungrow api get getInvertDataList
GoSungrow api get getModuleLogTaskList
GoSungrow api get powerDevicePointList
GoSungrow api get getPowerDevicePointNames '{"device_type":"1"}'
GoSungrow api get getPowerDevicePointNames '{"device_type":"2"}'
GoSungrow api get getPowerDevicePointNames '{"device_type":"7"}'
GoSungrow api get getDeviceModelInfoList
GoSungrow api get queryUnitList
GoSungrow api get getPowerSettingCharges
GoSungrow api get getPowerDevicePointNames '{"device_type":"1"}'
GoSungrow api get getPowerDeviceModelTechList '{"device_type":"1"}'
GoSungrow api get getPowerDevicePointInfo '{"id":"1"}'
## Task commands
GoSungrow api get queryBatchCreatePsTaskList
GoSungrow api get getPowerDeviceSetTaskDetailList '{"query_type":"2","task_id":"1","uuid":"844763"}'
GoSungrow api get getPowerDeviceSetTaskDetailList '{"query_type":"7","task_id":"1","uuid":"844763"}'
GoSungrow api get getPowerDeviceSetTaskDetailList '{"size":0,"curPage":0}'
GoSungrow api get getPowerDeviceSetTaskList '{"size":0,"curPage":0}'
GoSungrow api get getPowerDeviceSetTaskList '{"size":0,"curPage":1}'
GoSungrow api get getRemoteUpgradeSubTasksList '{"query_type":"1","task_id":"1577700"}'
GoSungrow api get getRemoteUpgradeTaskList '{"ps_id_list":"1171348"}'
## Misc commands
GoSungrow api get getDevicePoints '{"point_id":"13003"}'
GoSungrow api get getPowerPictureList
## Hacking
GoSungrow api get checkUnitStatus
GoSungrow api get getAllPowerDeviceSetName
GoSungrow api get getAreaList
GoSungrow api get getCloudList
GoSungrow api get getConfigList
GoSungrow api get getDeviceInfo
GoSungrow api get getOSSConfig
GoSungrow api get getOssObjectStream
GoSungrow api get getEncryptPublicKey
GoSungrow api get getFaultCount
GoSungrow api get getFaultDetail '{"fault_code":"34"}'
GoSungrow api get getFaultMsgByFaultCode '{"fault_code":"703"}'
GoSungrow api get getFaultMsgListWithYYYYMM

View File

@ -1,6 +1,6 @@
{
"appkey": "93D72E60331ABDCDC7B39ADC2D1F32B3",
"config": "/Users/mick/.GoSungrow/config.json",
"config": "",
"debug": false,
"host": "https://augateway.isolarcloud.com",
"password": "",
@ -8,4 +8,4 @@
"timeout": "30s",
"token-expiry": "",
"user": ""
}
}

View File

@ -0,0 +1,341 @@
# Examples of all known working commands
## Authentication
GoSungrow api login
## High-level info commands
```
Usage:
GoSungrow info [command]
Available Commands:
get Info - Get info from iSolarCloud (table)
raw Info - Get info from iSolarCloud (raw)
json Info - Get info from iSolarCloud (json)
csv Info - Get info from iSolarCloud (json)
put Info - Set info on iSolarCloud
```
```
Usage:
GoSungrow info get [command]
Available Commands:
point-names Info - Get iSolarCloud point names.
mqtt Info - Get iSolarCloud MQTT service login details.
search-point-names Info - Get iSolarCloud search point names.
devices Info - Get iSolarCloud devices.
models Info - Get ALL iSolarCloud models.
templates Info - Get all defined templates.
template-points Info - List data points used in report template.
device-points Info - List all available device data points.
```
## Get device details
GoSungrow info get device
GoSungrow info get search-point-names
GoSungrow info get models
## Templates
```
GoSungrow info get templates
┏━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓
┃ Template Id ┃ Template Name ┃ Update On ┃
┣━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━┫
┃ 8042 │ Critical │ 2022-02-15 13:00:28 ┃
┃ 8041 │ extras │ 2022-02-15 09:40:04 ┃
┃ 8036 │ C │ 2022-02-15 09:31:35 ┃
┃ 8039 │ v │ 2022-02-15 09:31:10 ┃
┃ 8040 │ A │ 2022-02-15 09:30:56 ┃
┃ 8034 │ Percent │ 2022-02-15 09:30:41 ┃
┃ 8038 │ MWh │ 2022-02-15 09:09:22 ┃
┃ 8037 │ MW │ 2022-02-15 09:03:22 ┃
┃ 8033 │ kW │ 2022-02-15 09:01:19 ┃
┃ 8035 │ Hours │ 2022-02-15 08:55:56 ┃
┃ 8031 │ kWh │ 2022-02-15 07:57:36 ┃
┃ 7981 │ Power │ 2022-02-09 10:03:40 ┃
┗━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━┛
```
## High-level data commands
```
Usage:
GoSungrow data [command]
Available Commands:
get Data - Get high-level data from iSolarCloud (table)
raw Data - Get high-level data from iSolarCloud (raw)
json Data - Get high-level data from iSolarCloud (json)
csv Data - Get high-level data from iSolarCloud (json)
graph Data - Get high-level data from iSolarCloud (graph)
```
```
Usage:
GoSungrow data get [command]
Available Commands:
stats Data - Get current inverter stats, (last 5 minutes).
template Data - Get data from report template.
points Data - Get points data for a specific date.
real-time Data - Get iSolarCloud real-time data.
psdetails Data - Get iSolarCloud ps details.
```
## General
```
GoSungrow data get stats
┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━┓
┃ Date ┃ Point Id ┃ Point Name ┃ Value ┃ Unit ┃
┣━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━┫
┃ 2022-10-08 09:55:00 │ 1129147.0 │ Co2 Reduce │ 0 │ kg ┃
┃ 2022-10-08 09:55:00 │ 1129147.0 │ Co2 Reduce Total │ 5819 │ kg ┃
┃ 2022-10-08 09:55:00 │ 1129147.0 │ Curr Power │ 788 │ W ┃
┃ 2022-10-08 09:55:00 │ 1129147.0 │ Daily Irradiation │ -- │ ┃
┃ 2022-10-08 09:55:00 │ 1129147.0 │ Equivalent Hour │ 0.27 │ Hour ┃
...
┃ 2022-10-08 09:55:00 │ 1171348.0 │ Today Income │ 0.397 │ AUD ┃
┃ 2022-10-08 09:55:00 │ 1171348.0 │ Total Capacity │ │ ┃
┃ 2022-10-08 09:55:00 │ 1171348.0 │ Total Energy │ 176.5 │ kWh ┃
┃ 2022-10-08 09:55:00 │ 1171348.0 │ Total Income │ 35.806 │ AUD ┃
┃ 2022-10-08 09:55:00 │ 1171348.0 │ Use Energy │ 6.7 │ kWh ┃
┗━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━┷━━━━━━┛
```
## Templates
```
GoSungrow info get template-points 7981
┏━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━┓
┃ PointStruct Id ┃ Description ┃ Unit ┃
┣━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━┫
┃ 1129147_11_0_0.p83032 │ Meter AC Power │ kW ┃
┃ 1129147_11_0_0.p83119 │ Daily Feed-in Energy (PV) │ kWh ┃
┃ 1129147_11_0_0.p83549 │ Grid active power │ kW ┃
┃ 1129147_11_0_0.p83002 │ Inverter AC Power │ kW ┃
┃ 1129147_11_0_0.p83011 │ Meter E-daily Consumption │ kWh ┃
┃ 1129147_11_0_0.p83022 │ Daily Yield of Plant │ kWh ┃
┃ 1129147_11_0_0.p83006 │ Meter Daily Yield │ kWh ┃
┃ 1129147_11_0_0.p83033 │ Plant Power │ kW ┃
┃ 1129147_11_0_0.p83072 │ Daily Feed-in Energy │ kWh ┃
┃ 1129147_11_0_0.p83097 │ Daily Load Energy Consumption from PV │ kWh ┃
┃ 1129147_11_0_0.p83102 │ Daily Purchased Energy │ kWh ┃
┃ 1129147_14_1_1.p13028 │ Daily Battery Charging Energy │ kWh ┃
┃ 1129147_14_1_1.p13112 │ Daily PV Yield │ kWh ┃
┃ 1129147_14_1_1.p13147 │ Daily Purchased Energy │ kWh ┃
┃ 1129147_14_1_1.p13173 │ Daily Feed-in Energy (PV) │ kWh ┃
┃ 1129147_14_1_1.p13174 │ Daily Battery Charging Energy from PV │ kWh ┃
┃ 1129147_14_1_1.p13199 │ Daily Load Energy Consumption │ kWh ┃
┃ 1129147_14_1_1.p13116 │ Daily Load Energy Consumption from PV │ kWh ┃
┃ 1129147_14_1_1.p13122 │ Daily Feed-in Energy │ kWh ┃
┗━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━┛
```
```
GoSungrow data get template 8042 20220212
┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━┓
┃ Date/Time ┃ Point Id ┃ Point Name ┃ Value ┃ Units ┃
┣━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━┫
┃ 2022-02-12 00:00:00 │ 1129147_11_0_0.p83106 │ Load Power │ 396 │ kW ┃
┃ 2022-02-12 00:05:00 │ 1129147_11_0_0.p83106 │ Load Power │ 480 │ kW ┃
┃ 2022-02-12 00:10:00 │ 1129147_11_0_0.p83106 │ Load Power │ 487 │ kW ┃
┃ 2022-02-12 00:15:00 │ 1129147_11_0_0.p83106 │ Load Power │ 497 │ kW ┃
┃ 2022-02-12 00:20:00 │ 1129147_11_0_0.p83106 │ Load Power │ 524 │ kW ┃
┃ 2022-02-12 00:25:00 │ 1129147_11_0_0.p83106 │ Load Power │ 541 │ kW ┃
┃ 2022-02-12 00:30:00 │ 1129147_11_0_0.p83106 │ Load Power │ 554 │ kW ┃
...
┃ 2022-02-12 23:55:00 │ 1129147_14_1_1.p13019 │ Internal Air Temperature │ 19.4 │ ℃ ┃
┃ 2022-02-13 00:00:00 │ 1129147_14_1_1.p13019 │ Internal Air Temperature │ 22.3 │ ℃ ┃
┗━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━┷━━━━━━━┛
```
GoSungrow data graph template 8042 20220212 '{"search_string":"p13019","min_left_axis":-6000,"max_left_axis":12000}'
GoSungrow data graph template 8042 20220212 '{"search_string":"p13019"}'
GoSungrow data graph template 8042 20220212 '{"search_string":"p83106","min_left_axis":-6000,"max_left_axis":12000}'
GoSungrow data graph template 8042 20220212 '{"search_string":"p83106"}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":"1","value_column":"4","search_column":"3","search_string":"p83106","file_name":"foo.png"}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":1,"value_column":4,"search_column":2,"search_string":"p13019","min_left_axis":0,"max_left_axis":0}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":1,"value_column":4,"search_column":2,"search_string":"p13149","min_left_axis":0,"max_left_axis":0}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":1,"value_column":4,"search_column":2,"search_string":"p83106","file_name":"foo.png"}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":1,"value_column":4,"search_column":2,"search_string":"p83106","min_left_axis":-6000,"max_left_axis":1000}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":1,"value_column":4,"search_column":2,"search_string":"p83106","min_left_axis":-6000,"max_left_axis":12000}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":1,"value_column":4,"search_column":2,"search_string":"p83106","min_left_axis":-6000,"max_left_axis":42000}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":1,"value_column":4,"search_column":2,"search_string":"p83106","min_left_axis":0,"max_left_axis":0}'
GoSungrow data graph template 8042 20220212 '{"title":"Testing 1. 2. 3.","time_column":1,"value_column":4,"search_column":3,"search_string":"p83106","file_name":"foo.png"}'
GoSungrow data raw template '' 20220201
GoSungrow data save template '' 20220201
GoSungrow data save template 8042 20220212
## Hacking
## Base API commands
```
Usage:
GoSungrow api [command]
Examples:
GoSungrow api login
GoSungrow api get <endpoint> <args>
GoSungrow api raw <endpoint> <args>
GoSungrow api save <endpoint> <args>
GoSungrow api put <endpoint> <args>
Available Commands:
ls List iSolarCloud api endpoints/areas
login Login to iSolarCloud
get Get details from iSolarCloud
raw Raw details from iSolarCloud
save Save details from iSolarCloud as JSON
put Put details onto iSolarCloud
```
## Get device details
GoSungrow api get getPsList
GoSungrow api get getPsListByName
GoSungrow api get getPsDataSupplementTaskList
GoSungrow api get getPsUser
GoSungrow api get queryPsIdList
GoSungrow api get getInstallInfoList
GoSungrow api get getPsListStaticData
GoSungrow api get queryAllPsIdAndName
GoSungrow api get getPsDetail '{"ps_id":"1171348"}'
GoSungrow api get getPsDetailWithPsType '{"ps_id":"1171348"}'
GoSungrow api get getPsHealthState '{"ps_id":"1171348"}'
GoSungrow api get getPsWeatherList '{"ps_id":"1171348"}'
GoSungrow api get queryDeviceList '{"ps_id":"1171348"}'
GoSungrow api get queryDeviceListByUserId '{"ps_id":"1171348"}'
GoSungrow api get queryDeviceListForApp '{"ps_id":"1171348"}'
GoSungrow api get findPsType '{"ps_id":"1171348"}'
GoSungrow api get getDeviceList '{"ps_id":"1171348"}'
GoSungrow api get getIncomeSettingInfos '{"ps_id":"1171348"}'
GoSungrow api get getPowerChargeSettingInfo '{"ps_id":"1171348"}'
GoSungrow api get getPowerStationInfo '{"ps_id":"1171348"}'
GoSungrow api get WebAppService.getDeviceUuid '{"ps_key":"1171348"}'
GoSungrow api get getPowerStationForHousehold '{"ps_id":"1171348"}'
## Get device data
GoSungrow api get energyTrend
GoSungrow api get getKpiInfo
GoSungrow api get queryPsProfit '{"date_id":"20221001","date_type":"1","ps_id":"1171348"}'
GoSungrow api get getPowerStationData '{"date_id":"20221001","date_type":"1","ps_id":"1171348"}'
GoSungrow api get getHouseholdStoragePsReport '{"date_id":"20221001","date_type":"1","ps_id":"1171348"}'
GoSungrow api get queryPsProfit '{"date_id":"202210","date_type":"2","ps_id":"1171348"}'
GoSungrow api get getPowerStationData '{"date_id":"202210","date_type":"2","ps_id":"1171348"}'
GoSungrow api get getHouseholdStoragePsReport '{"date_id":"202210","date_type":"2","ps_id":"1171348"}'
GoSungrow api get queryPsProfit '{"date_id":"2022","date_type":"3","ps_id":"1171348"}'
GoSungrow api get getPowerStationData '{"date_id":"2022","date_type":"3","ps_id":"1171348"}'
GoSungrow api get getHouseholdStoragePsReport '{"date_id":"2022","date_type":"3","ps_id":"1171348"}'
GoSungrow api get getPowerTrendDayData '{"BeginTime":"20221004"}'
GoSungrow api get getPowerStatistics '{"ps_id":"1171348"}'
GoSungrow api get WebAppService.showPSView '{"ps_id":"1171348"}'
## Reports
GoSungrow api get getPsReport '{"report_type":"1","date_id":"20220201","date_type":"1","ps_id":"1171348"}'
GoSungrow api get getPsReport '{"report_type":"1","date_id":"20220201","date_type":"2","ps_id":"1171348"}'
GoSungrow api get getPsReport '{"report_type":"1","date_id":"20220201","date_type":"3","ps_id":"1171348"}'
GoSungrow api get reportList '{"ps_id":"1171348","report_type":"1"}'
GoSungrow api get reportList '{"ps_id":"1171348","report_type":"2"}'
GoSungrow api get reportList '{"ps_id":"1171348","report_type":"3"}'
GoSungrow api get reportList '{"ps_id":"1171348","report_type":"4"}'
## Templates
GoSungrow api get template 20220202
GoSungrow api get template 8042 20220212
GoSungrow api get getTemplateList
GoSungrow api get WebAppService.queryUserCurveTemplateData '{"template_id":"8042","date_type":"1","start_time":"20220223000000","end_time":"20220223235900"}'
## User/installer/support info
GoSungrow api get getUserList
GoSungrow api get getUserPsOrderList
GoSungrow api get getPhotoInfo
GoSungrow api get getOrgListByName
GoSungrow api get getOrgListByUserId
GoSungrow api get getOrgListForUser
GoSungrow api get queryUserList
GoSungrow api get getInstallerInfoByDealerOrgCodeOrId '{"dealer_org_code":"AUSCEKK7"}'
GoSungrow api get getInstallerInfoByDealerOrgCodeOrId '{"org_id":"362245"}'
GoSungrow api get getInstallerInfoByDealerOrgCodeOrId '{"org_id":"80384"}'
GoSungrow api get getInstallerInfoByDealerOrgCodeOrId '{"org_id":"80393"}'
GoSungrow api get getInstallerInfoByDealerOrgCodeOrId '{"org_id":"300977"}'
## Meta-data
GoSungrow api get getDeviceTypeInfoList
GoSungrow api get getDeviceTypeList
GoSungrow api get getInvertDataList
GoSungrow api get getModuleLogTaskList
GoSungrow api get powerDevicePointList
GoSungrow api get getPowerDevicePointNames '{"device_type":"1"}'
GoSungrow api get getPowerDevicePointNames '{"device_type":"2"}'
GoSungrow api get getPowerDevicePointNames '{"device_type":"7"}'
GoSungrow api get getDeviceModelInfoList
GoSungrow api get queryUnitList
GoSungrow api get getPowerSettingCharges
GoSungrow api get getPowerDevicePointNames '{"device_type":"1"}'
GoSungrow api get getPowerDeviceModelTechList '{"device_type":"1"}'
GoSungrow api get getPowerDevicePointInfo '{"id":"1"}'
## Task commands
GoSungrow api get queryBatchCreatePsTaskList
GoSungrow api get getPowerDeviceSetTaskDetailList '{"query_type":"2","task_id":"1","uuid":"844763"}'
GoSungrow api get getPowerDeviceSetTaskDetailList '{"query_type":"7","task_id":"1","uuid":"844763"}'
GoSungrow api get getPowerDeviceSetTaskDetailList '{"size":0,"curPage":0}'
GoSungrow api get getPowerDeviceSetTaskList '{"size":0,"curPage":0}'
GoSungrow api get getPowerDeviceSetTaskList '{"size":0,"curPage":1}'
GoSungrow api get getRemoteUpgradeSubTasksList '{"query_type":"1","task_id":"1577700"}'
GoSungrow api get getRemoteUpgradeTaskList '{"ps_id_list":"1171348"}'
## Misc commands
GoSungrow api get getDevicePoints '{"point_id":"13003"}'
GoSungrow api get getPowerPictureList
## Hacking
GoSungrow api get checkUnitStatus
GoSungrow api get getAllPowerDeviceSetName
GoSungrow api get getAreaList
GoSungrow api get getCloudList
GoSungrow api get getConfigList
GoSungrow api get getDeviceInfo
GoSungrow api get getOSSConfig
GoSungrow api get getOssObjectStream
GoSungrow api get getEncryptPublicKey
GoSungrow api get getFaultCount
GoSungrow api get getFaultDetail '{"fault_code":"34"}'
GoSungrow api get getFaultMsgByFaultCode '{"fault_code":"703"}'
GoSungrow api get getFaultMsgListWithYYYYMM

View File

@ -15,7 +15,7 @@ var Examples string
const (
Description = "GoSungrow - GoLang implementation to access the iSolarCloud API updated by SunGrow inverters"
BinaryName = "GoSungrow"
BinaryVersion = "2.2.1"
BinaryVersion = "2.3.0"
SourceRepo = "github.com/MickMake/" + BinaryName
BinaryRepo = "github.com/MickMake/" + BinaryName

View File

@ -1,3 +1,6 @@
GoSungrow api login
GoSungrow api ls areas
GoSungrow api get WebAppService.getDeviceUuid '{"ps_key":"1171348"}'
GoSungrow api get WebAppService.showPSView '{"ps_id":"1171348"}'
GoSungrow api get checkUnitStatus
@ -53,8 +56,6 @@ GoSungrow api get reportList '{"ps_id":"1171348","report_type":"1"}'
GoSungrow api get reportList '{"ps_id":"1171348","report_type":"2"}'
GoSungrow api get reportList '{"ps_id":"1171348","report_type":"3"}'
GoSungrow api get reportList '{"ps_id":"1171348","report_type":"4"}'
GoSungrow api login
GoSungrow api ls areas
GoSungrow api raw queryDeviceList '{"ps_id":"1171348"}'
GoSungrow api save WebAppService.queryUserCurveTemplateData '{"template_id":"8042","date_type":"1","start_time":"20220223000000","end_time":"20220223235900"}'
GoSungrow api save getPsDetail '{"ps_id":""}'
@ -72,6 +73,7 @@ GoSungrow data get template 8042 20220227
GoSungrow data get template-points 8040
GoSungrow data get template-points 8041
GoSungrow data get template-points 8042
GoSungrow data graph template 8042 20220212 '{"search_string":"p13019","min_left_axis":-6000,"max_left_axis":12000}'
GoSungrow data graph template 8042 20220212 '{"search_string":"p13019"}'
GoSungrow data graph template 8042 20220212 '{"search_string":"p83106","min_left_axis":-6000,"max_left_axis":12000}'
@ -90,21 +92,17 @@ GoSungrow data save template '' 20220201
GoSungrow data save template 8042 20220212
GoSungrow get raw template 20220202
GoSungrow get template 8042 20220212
GoSungrow data get stats
GoSungrow data get search-point-names
GoSungrow data get device
GoSungrow data get model
GoSungrow api raw getPowerStationForHousehold '{"ps_id":"1171348"}'
> "org_index_code":"|80384|80393|300977|",
GoSungrow api raw getInstallerInfoByDealerOrgCodeOrId '{"dealer_org_code":"AUSCEKK7"}'
GoSungrow api raw getInstallerInfoByDealerOrgCodeOrId '{"org_id":"362245"}'
GoSungrow api raw getInstallerInfoByDealerOrgCodeOrId '{"org_id":"80384"}'
GoSungrow api raw getInstallerInfoByDealerOrgCodeOrId '{"org_id":"80393"}'
GoSungrow api raw getInstallerInfoByDealerOrgCodeOrId '{"org_id":"300977"}'
GoSungrow api raw getKpiInfo
GoSungrow api raw getInstallInfoList
GoSungrow api raw getIncomeSettingInfos '{"ps_id":"1171348"}'
@ -118,43 +116,22 @@ GoSungrow api raw getOrgListForUser
GoSungrow api raw getPowerDevicePointNames '{"device_type":"1"}'
GoSungrow api raw getPowerDeviceModelTechList '{"device_type":"1"}'
GoSungrow api raw getPowerDevicePointInfo '{"id":"1"}'
GoSungrow api raw getPowerStationForHousehold '{"ps_id":"1171348"}'
GoSungrow api raw queryPsProfit '{"date_id":"20221001","date_type":"1","ps_id":"1171348"}'
GoSungrow api raw getPowerStationData '{"date_id":"20221001","date_type":"1","ps_id":"1171348"}'
GoSungrow api get getHouseholdStoragePsReport '{"date_id":"20221001","date_type":"1","ps_id":"1171348"}'
GoSungrow api raw queryPsProfit '{"date_id":"202210","date_type":"2","ps_id":"1171348"}'
GoSungrow api raw getPowerStationData '{"date_id":"202210","date_type":"2","ps_id":"1171348"}'
GoSungrow api get getHouseholdStoragePsReport '{"date_id":"202210","date_type":"2","ps_id":"1171348"}'
GoSungrow api raw queryPsProfit '{"date_id":"2022","date_type":"3","ps_id":"1171348"}'
GoSungrow api raw getPowerStationData '{"date_id":"2022","date_type":"3","ps_id":"1171348"}'
GoSungrow api get getHouseholdStoragePsReport '{"date_id":"2022","date_type":"3","ps_id":"1171348"}'
GoSungrow api raw getPowerStationInfo '{"ps_id":"1171348"}'
GoSungrow api raw getPowerTrendDayData '{"BeginTime":"20221004"}'
GoSungrow api raw getPowerSettingCharges
GoSungrow api raw queryUnitList
GoSungrow api raw queryPsIdList
GoSungrow api get getEncryptPublicKey
GoSungrow api raw getFaultCount
GoSungrow api raw getFaultDetail '{"fault_code":"34"}'
GoSungrow api raw getFaultMsgByFaultCode '{"fault_code":"703"}'
GoSungrow api get getFaultMsgListWithYYYYMM
################################################################################
"energy_flow"
1 / 3 - PV2Load && PV2Battery
"1" - PV to Load
"3" - Battery to Load
"5" - PV to Battery
https://augateway.isolarcloud.com/v1/commonService/getMqttConfigInfoByAppkey
sensor.sungrow_total_load_active_power == sensor.sungrow_total_active_power

View File

@ -1,9 +0,0 @@
package google
const (
DefaultGoogleClientId = "424242424242-42424242424242424242424242424242.apps.googleusercontent.com"
DefaultGoogleClientSecret = "424242424242424242424242"
DefaultGoogleCredentials = `{"installed":{"client_id":"424242424242-42424242424242424242424242424242.apps.googleusercontent.com","project_id":"isolarcloud","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"424242424242424242424242","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}}
`
DefaultAuthTokenFile = "GoogleSheetsAuthToken.json"
)

View File

@ -1,111 +0,0 @@
package google
import (
"GoSungrow/Only"
"context"
"encoding/json"
"errors"
"fmt"
"golang.org/x/oauth2"
"net/http"
"os"
)
// Retrieve a token, saves the token, then returns the generated client.
func (s *Sheet) getClient() (*http.Client, error) {
var ret *http.Client
var err error
for range Only.Once {
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
var err error
err = s.tokenFromFile()
if err != nil {
err = s.getTokenFromWeb()
if err != nil {
break
}
err = s.saveToken()
if err != nil {
break
}
}
ret = s.oAuthConfig.Client(context.Background(), s.token)
}
return ret, err
}
// RequestCommon a token from the web, then returns the retrieved token.
func (s *Sheet) getTokenFromWeb() error {
var err error
for range Only.Once {
authURL := s.oAuthConfig.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
fmt.Printf("Allow access to this application by going to this URL:\n%v\n", authURL)
fmt.Printf("\nThen copy-paste the authorization code here: ")
var authCode string
_, err = fmt.Scan(&authCode)
if err != nil {
err = errors.New(fmt.Sprintf("Unable to read authorization code: %v", err))
break
}
s.token, err = s.oAuthConfig.Exchange(context.TODO(), authCode)
if err != nil {
err = errors.New(fmt.Sprintf("Unable to retrieve token from web: %v", err))
break
}
}
return err
}
// Retrieves a token from a local file.
func (s *Sheet) tokenFromFile() error {
var err error
for range Only.Once {
if s.TokenFile == "" {
err = errors.New("Empty token filename")
break
}
var f *os.File
f, err = os.Open(s.TokenFile)
if err != nil {
break
}
defer f.Close()
//ret = &oauth2.token{}
err = json.NewDecoder(f).Decode(s.token)
}
return err
}
// Saves a token to a file path.
func (s *Sheet) saveToken() error {
var err error
for range Only.Once {
fmt.Printf("Saving token file to: %s\n", s.TokenFile)
var f *os.File
f, err = os.OpenFile(s.TokenFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
err = errors.New(fmt.Sprintf("Unable to cache oauth token: %v", err))
break
}
defer f.Close()
err = json.NewEncoder(f).Encode(s.token)
}
return err
}

View File

@ -1,193 +0,0 @@
package google
import (
"GoSungrow/Only"
"context"
"errors"
"fmt"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/googleapi"
"google.golang.org/api/option"
"google.golang.org/api/sheets/v4"
"net/http"
"os"
"path/filepath"
)
const DefaultId = "SUPERSECRETKEY"
type Sheet struct {
Id string
SheetName string
Range string
Credentials []byte
TokenFile string
token *oauth2.Token
oAuthConfig *oauth2.Config
Data SheetData
}
type SheetData [][]interface{}
func (s *Sheet) Set(cfg Sheet) {
for range Only.Once {
s.Id = cfg.Id
s.SheetName = cfg.SheetName
s.Range = cfg.Range
s.Credentials = cfg.Credentials
s.TokenFile = cfg.TokenFile
s.token = cfg.token
s.Verify()
}
}
func (s *Sheet) Verify() bool {
var ok bool
for range Only.Once {
if s.Id == "" {
s.Id = DefaultId
}
if s.SheetName == "" {
s.SheetName = ""
}
if s.Range == "" {
s.Range = "A1"
}
if len(s.Credentials) == 0 {
s.Credentials = []byte(DefaultGoogleCredentials)
}
// token *oauth2.token
// oAuthConfig *oauth2.Config
if s.oAuthConfig == nil {
s.oAuthConfig = &oauth2.Config{}
}
if s.token == nil {
s.token = &oauth2.Token{}
}
if len(s.TokenFile) == 0 {
var err error
s.TokenFile, err = os.UserHomeDir()
if err != nil {
s.TokenFile = ""
break
}
s.TokenFile = filepath.Join(s.TokenFile, ".GoSungrow", DefaultAuthTokenFile)
}
ok = true
}
return ok
}
func (s *Sheet) ReadSheet() (SheetData, error) {
var err error
var csv SheetData
for range Only.Once {
if !s.Verify() {
break
}
// If modifying these scopes, delete your previously saved token.json.
s.oAuthConfig, err = google.ConfigFromJSON(s.Credentials, sheets.SpreadsheetsReadonlyScope)
if err != nil {
err = errors.New(fmt.Sprintf("Unable to parse client secret file to config: %v", err))
break
}
var client *http.Client
client, err = s.getClient()
ctx := context.Background()
var srv *sheets.Service
srv, err = sheets.NewService(ctx, option.WithHTTPClient(client))
if err != nil {
err = errors.New(fmt.Sprintf("Unable to retrieve Sheets client: %v", err))
break
}
readRange := fmt.Sprintf("%s!%s", s.SheetName, s.Range)
var resp *sheets.ValueRange
resp, err = srv.Spreadsheets.Values.Get(s.Id, readRange).Do()
if err != nil {
err = errors.New(fmt.Sprintf("Unable to retrieve data from sheet: %v", err))
break
}
if len(resp.Values) == 0 {
fmt.Println("No data found.")
break
}
for _, row := range resp.Values {
// Print columns A and E, which correspond to indices 0 and 4.
fmt.Printf("%v\n", row)
}
}
return csv, err
}
func (s *Sheet) WriteSheet() error {
var err error
for range Only.Once {
if !s.Verify() {
break
}
// If modifying these scopes, delete your previously saved token.json.
s.oAuthConfig, err = google.ConfigFromJSON(s.Credentials, sheets.SpreadsheetsScope)
if err != nil {
err = errors.New(fmt.Sprintf("Unable to parse client secret file to config: %v", err))
break
}
var client *http.Client
client, err = s.getClient()
//client = getClient(s.oAuthConfig)
ctx := context.Background()
var srv *sheets.Service
srv, err = sheets.NewService(ctx, option.WithHTTPClient(client))
if err != nil {
err = errors.New(fmt.Sprintf("Unable to retrieve Sheets client: %v", err))
break
}
//var itt = srv.Spreadsheets.getSheetByName('_EmailList');
//if (!itt) {
// ss.insertSheet('_EmailList');
//}
vr := sheets.ValueRange{
MajorDimension: "",
Range: s.SheetName + "!" + s.Range,
Values: s.Data,
ServerResponse: googleapi.ServerResponse{},
ForceSendFields: nil,
NullFields: nil,
}
writeRange := fmt.Sprintf("%s!%s", s.SheetName, s.Range)
fmt.Printf("Updating Google sheet '%s'.\n\tWorksheet: %s\n\tStarting from: %s\n",
s.Id,
s.SheetName,
s.Range,
)
_, err = srv.Spreadsheets.Values.Update(s.Id, writeRange, &vr).ValueInputOption("USER_ENTERED").Do() // or "RAW"
if err != nil {
err = errors.New(fmt.Sprintf("Unable to retrieve data from sheet. %v", err))
break
}
}
return err
}

View File

@ -11,7 +11,7 @@ const Url = "/v1/devService/getDeviceList"
const Disabled = false
type RequestData struct {
PsId int64 `json:"ps_id" required:"true"`
PsId string `json:"ps_id" required:"true"`
}
func (rd RequestData) IsValid() error {
@ -26,7 +26,7 @@ func (rd RequestData) Help() string {
type ResultData struct {
PageList []struct {
AttrID int64 `json:"attr_id"`
ChnnlID int64 `json:"chnnl_id"`
ChannelId int64 `json:"chnnl_id"`
CommandStatus int64 `json:"command_status"`
ConnectState int64 `json:"connect_state"`
DataFlag int64 `json:"data_flag"`
@ -132,7 +132,7 @@ func (e *EndPoint) GetDataTable() output.Table {
d.PsID,
d.DeviceType,
d.DeviceCode,
d.ChnnlID,
d.ChannelId,
d.TypeName,
d.DeviceProSn,
d.DeviceModel,

View File

@ -41,15 +41,15 @@ type ResultData struct {
CuspPowerQuantity interface{} `json:"cusp_power_quantity"`
CuspUsePowerQuantity interface{} `json:"cusp_use_power_quantity"`
DateID int64 `json:"date_id"`
FlatNetPowerQuantity int64 `json:"flat_net_power_quantity"`
FlatPowerQuantity int64 `json:"flat_power_quantity"`
FlatNetPowerQuantity float64 `json:"flat_net_power_quantity"`
FlatPowerQuantity float64 `json:"flat_power_quantity"`
FlatUsePowerQuantity float64 `json:"flat_use_power_quantity"`
NetPowerProfit float64 `json:"net_power_profit"`
NetPowerQuantityTotal int64 `json:"net_power_quantity_total"`
NetPowerQuantityTotal float64 `json:"net_power_quantity_total"`
PeakNetPowerQuantity interface{} `json:"peak_net_power_quantity"`
PeakPowerQuantity interface{} `json:"peak_power_quantity"`
PeakUsePowerQuantity interface{} `json:"peak_use_power_quantity"`
PowerQuantityTotal int64 `json:"power_quantity_total"`
PowerQuantityTotal float64 `json:"power_quantity_total"`
SubsidyProfit interface{} `json:"subsidy_profit"`
TotalProfit float64 `json:"total_profit"`
UpdateTime string `json:"update_time"`

View File

@ -97,7 +97,7 @@ type ResultData struct {
SubsidyProfitOriginalUnit string `json:"subsidy_profit_original_unit"`
SubsidyProfitTran string `json:"subsidy_profit_tran"`
SubsidyProfitUnit string `json:"subsidy_profit_unit"`
TimeStamp string `json:"time_stamp"`
TimeStamp interface{} `json:"time_stamp"` // Sad that this alternates between string and int64.
TotalProfit string `json:"total_profit"`
TotalProfitOriginalUnit string `json:"total_profit_original_unit"`
TotalProfitTran string `json:"total_profit_tran"`

View File

@ -113,8 +113,8 @@ func (sg *SunGrow) GetTemplateData(template string, date string, filter string)
break
}
table.SetTitle("Template %s on %s for %s", template, when.String(), psId)
table.SetFilePrefix(data.SetFilenamePrefix("%s-%s-%s", when.String(), template, psId))
table.SetTitle("Template %s on %s for %d", template, when.String(), psId)
table.SetFilePrefix(data.SetFilenamePrefix("%s-%s-%d", when.String(), template, psId))
table.SetGraphFilter(filter)
table.SetSaveFile(sg.SaveAsFile)
table.OutputType = sg.OutputType
@ -694,7 +694,7 @@ func (sg *SunGrow) GetDevices(psIds ...int64) error {
for _, psId := range psIds {
ep := sg.GetByStruct(
"AppService.getDeviceList",
getDeviceList.RequestData{PsId: psId},
getDeviceList.RequestData{PsId: strconv.FormatInt(psId, 10)},
DefaultCacheTimeout,
)
if sg.Error != nil {

View File

@ -1,72 +0,0 @@
package lsgo
import (
"log"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
// declare the struct that holds all the arguments
type arguments struct {
paths *[]string
all *bool
bytes *bool
mdate *bool
owner *bool
nogroup *bool
perms *bool
long *bool
dirs *bool
files *bool
links *bool
linkRel *bool
sortSize *bool
sortTime *bool
sortKind *bool
backwards *bool
stats *bool
icons *bool
nerdfont *bool
recurse *bool
find *string
}
var args = arguments{
kingpin.Arg("paths", "the files(s) and/or folder(s) to display").Default(".").Strings(),
kingpin.Flag("all", "show hidden files").Short('a').Bool(),
kingpin.Flag("bytes", "include size").Short('b').Bool(),
kingpin.Flag("mdate", "include modification date").Short('m').Bool(),
kingpin.Flag("owner", "include owner and group").Short('o').Bool(),
kingpin.Flag("nogroup", "hide group").Short('N').Bool(),
kingpin.Flag("perms", "include permissions for owner, group, and other").Short('p').Bool(),
kingpin.Flag("long", "include size, date, owner, and permissions").Short('l').Bool(),
kingpin.Flag("dirs", "only show directories").Short('d').Bool(),
kingpin.Flag("files", "only show files").Short('f').Bool(),
kingpin.Flag("links", "show paths for symlinks").Short('L').Bool(),
kingpin.Flag("link-rel", "show symlinks as relative paths if shorter than absolute path").Short('R').Bool(),
kingpin.Flag("size", "sort items by size").Short('s').Bool(),
kingpin.Flag("time", "sort items by time").Short('t').Bool(),
kingpin.Flag("kind", "sort items by extension").Short('k').Bool(),
kingpin.Flag("backwards", "reverse the sort order of --size, --time, or --kind").Short('B').Bool(),
kingpin.Flag("stats", "show statistics").Short('S').Bool(),
kingpin.Flag("icons", "show folder icon before dirs").Short('i').Bool(),
kingpin.Flag("nerd-font", "show nerd font glyphs before file names").Short('n').Bool(),
kingpin.Flag("recurse", "traverse all dirs recursively").Short('r').Bool(),
kingpin.Flag("find", "filter items with a regexp").Short('F').String(),
}
func argsPostParse() {
if *args.long {
args.bytes = &True
args.mdate = &True
args.owner = &True
args.perms = &True
args.links = &True
}
if *args.dirs && *args.files {
log.Fatal("--dirs and --files cannot both be set")
}
if *args.nerdfont && *args.icons {
log.Fatal("--nerd-font and --icons cannot both be set")
}
}

View File

@ -1,351 +0,0 @@
package lsgo
import (
"fmt"
"strconv"
"strings"
)
// see the color codes
// http://i.stack.imgur.com/UQVe5.png
// Fg wraps an 8-bit foreground color code in the ANSI escape sequence
func Fg(code int) string {
colored := []string{"\x1b[38;5;", strconv.Itoa(code), "m"}
return strings.Join(colored, "")
}
// Bg wraps an 8-bit background color code in the ANSI escape sequence
func Bg(code int) string {
colored := []string{"\x1b[48;5;", strconv.Itoa(code), "m"}
return strings.Join(colored, "")
}
// Rgb2code converts RGB values (up to 5) to an 8-bit color code
func Rgb2code(r int, g int, b int) int {
code := 36*r + 6*g + b + 16
if code < 16 || 231 < code {
panic(fmt.Errorf("Invalid RGB values (%d, %d, %d)", r, g, b))
}
return code
}
// Gray2code converts a scalar of "grayness" to an 8-bit color code
func Gray2code(lightness int) int {
code := lightness + 232
if code < 232 || 255 < code {
panic(fmt.Errorf("Invalid lightness value (%d) for gray", lightness))
}
return code
}
// FgRGB converts RGB values (up to 5) to an ANSI-escaped foreground 8-bit color code
func FgRGB(r int, g int, b int) string {
return Fg(Rgb2code(r, g, b))
}
// BgRGB converts RGB values (up to 5) to an ANSI-escaped background 8-bit color code
func BgRGB(r int, g int, b int) string {
return Bg(Rgb2code(r, g, b))
}
// FgGray converts a scalar of "grayness" to an ANSI-escaped foreground 8-bit color code
func FgGray(lightness int) string {
return Fg(Gray2code(lightness))
}
// BgGray converts a scalar of "grayness" to an ANSI-escaped background 8-bit color code
func BgGray(lightness int) string {
return Bg(Gray2code(lightness))
}
const (
// Reset undoes ANSI color codes
Reset = "\x1b[0m"
// Bold makes text bold
Bold = "\x1b[1m"
)
var (
// FileColor is a mapping of filetypes to colors
FileColor = map[string][2]string{
"as": [2]string{Fg(196), Fg(124)},
"asm": [2]string{Fg(223), Fg(215)},
"bf": [2]string{Fg(223), Fg(215)},
"c": [2]string{Fg(39), Fg(27)},
"clj": [2]string{Fg(204), Fg(162)},
"coffee": [2]string{Fg(136), Fg(94)},
"cr": [2]string{Fg(82), Fg(70)},
"cson": [2]string{Fg(136), Fg(94)},
"css": [2]string{Fg(219), Fg(207)},
"dart": [2]string{Fg(43), Fg(31)},
"diff": [2]string{Fg(10), Fg(9)},
"elm": [2]string{Fg(51), Fg(39)},
"erl": [2]string{Fg(161), Fg(89)},
"ex": [2]string{Fg(99), Fg(57)},
"f": [2]string{Fg(208), Fg(94)},
"fs": [2]string{Fg(45), Fg(32)},
"gb": [2]string{Fg(43), Fg(29)},
"go": [2]string{Fg(121), Fg(109)},
"graphql": [2]string{Fg(219), Fg(207)},
"groovy": [2]string{Fg(223), Fg(215)},
"gv": [2]string{Fg(141), Fg(99)},
"hs": [2]string{Fg(99), Fg(57)},
"html": [2]string{Fg(87), Fg(73)},
"ino": [2]string{Fg(43), Fg(29)},
"java": [2]string{Fg(136), Fg(94)},
"jl": [2]string{Fg(141), Fg(99)},
"js": [2]string{FgRGB(4, 4, 0), FgRGB(2, 2, 0)},
"jsx": [2]string{Fg(87), Fg(73)},
"lock": [2]string{FgGray(8), FgGray(5)},
"log": [2]string{FgGray(8), FgGray(5)},
"lua": [2]string{Fg(39), Fg(27)},
"m": [2]string{Fg(208), Fg(196)},
"md": [2]string{Fg(87), Fg(73)},
"ml": [2]string{Fg(208), Fg(94)},
"php": [2]string{Fg(30), Fg(22)},
"pl": [2]string{Fg(99), Fg(57)},
"py": [2]string{Fg(34), Fg(28)},
"r": [2]string{Fg(51), Fg(39)},
"rb": [2]string{FgRGB(5, 1, 0), FgRGB(3, 1, 1)},
"rs": [2]string{Fg(208), Fg(94)},
"scala": [2]string{Fg(196), Fg(124)},
"sh": [2]string{FgRGB(4, 0, 4), FgRGB(2, 0, 2)},
"sol": [2]string{Fg(39), Fg(27)},
"sql": [2]string{Fg(193), Fg(148)},
"svelte": [2]string{Fg(208), Fg(196)},
"swift": [2]string{Fg(223), Fg(215)},
"vim": [2]string{Fg(34), Fg(28)},
"vue": [2]string{Fg(43), Fg(29)},
"xml": [2]string{Fg(87), Fg(73)},
"compiled": [2]string{FgGray(8), FgGray(5)},
"compress": [2]string{FgRGB(5, 0, 0), FgRGB(3, 0, 0)},
"document": [2]string{FgRGB(5, 0, 0), FgRGB(3, 0, 0)},
"media": [2]string{Fg(141), Fg(99)},
"_default": [2]string{FgGray(23), FgGray(12)},
}
// FileAliases converts alternative extensions to their canonical mapping in FileColor
FileAliases = map[string]string{
"s": "asm",
"b": "bf",
"c++": "c",
"cc": "c",
"cpp": "c",
"cs": "c",
"cxx": "c",
"d": "c",
"h": "c",
"h++": "c",
"hh": "c",
"hpp": "c",
"hxx": "c",
"pxd": "c",
"cljc": "clj",
"cljs": "clj",
"class": "compiled",
"elc": "compiled",
"hi": "compiled",
"o": "compiled",
"pyc": "compiled",
"7z": "compress",
"Z": "compress",
"bz2": "compress",
"deb": "compress",
"dmg": "compress",
"dpkg": "compress",
"gz": "compress",
"iso": "compress",
"jar": "compress",
"lzma": "compress",
"par": "compress",
"rar": "compress",
"rpm": "compress",
"tar": "compress",
"tc": "compress",
"tgz": "compress",
"txz": "compress",
"whl": "compress",
"xz": "compress",
"z": "compress",
"zip": "compress",
"less": "css",
"sass": "css",
"scss": "css",
"styl": "css",
"djvu": "document",
"doc": "document",
"docx": "document",
"dvi": "document",
"eml": "document",
"fotd": "document",
"odp": "document",
"odt": "document",
"pdf": "document",
"ppt": "document",
"pptx": "document",
"rtf": "document",
"xls": "document",
"xlsx": "document",
"f03": "f",
"f77": "f",
"f90": "f",
"f95": "f",
"for": "f",
"fpp": "f",
"ftn": "f",
"fsi": "fs",
"fsscript": "fs",
"fsx": "fs",
"dna": "gb",
"gsh": "groovy",
"gvy": "groovy",
"gy": "groovy",
"htm": "html",
"xhtml": "html",
"bson": "js",
"json": "js",
"mjs": "js",
"ts": "js",
"tsx": "jsx",
"cjsx": "jsx",
"mat": "m",
"markdown": "md",
"mkd": "md",
"rst": "md",
"sml": "ml",
"mli": "ml",
"aac": "media",
"alac": "media",
"audio": "media",
"avi": "media",
"bmp": "media",
"cbr": "media",
"cbz": "media",
"eps": "media",
"flac": "media",
"flv": "media",
"gif": "media",
"ico": "media",
"image": "media",
"jpeg": "media",
"jpg": "media",
"m2v": "media",
"m4a": "media",
"mka": "media",
"mkv": "media",
"mov": "media",
"mp3": "media",
"mp4": "media",
"mpeg": "media",
"mpg": "media",
"nef": "media",
"ogg": "media",
"ogm": "media",
"ogv": "media",
"opus": "media",
"orf": "media",
"pbm": "media",
"pgm": "media",
"png": "media",
"pnm": "media",
"ppm": "media",
"pxm": "media",
"stl": "media",
"svg": "media",
"tif": "media",
"tiff": "media",
"video": "media",
"vob": "media",
"wav": "media",
"webm": "media",
"webp": "media",
"wma": "media",
"wmv": "media",
"xpm": "media",
"php3": "php",
"php4": "php",
"php5": "php",
"phpt": "php",
"phtml": "php",
"ipynb": "py",
"pickle": "py",
"pkl": "py",
"pyx": "py",
"bash": "sh",
"csh": "sh",
"fish": "sh",
"ksh": "sh",
"zsh": "sh",
"plpgsql": "sql",
"plsql": "sql",
"psql": "sql",
"tsql": "sql",
"vimrc": "vim",
}
// SizeColor has the color mappings for ranges of file sizes
SizeColor = map[string]string{
"B": Fg(27),
"K": Fg(33),
"M": Fg(81),
"G": Fg(123),
"T": Fg(159),
}
// ConfigColor holds mappings for other various colors
ConfigColor = map[string]map[string]string{
"dir": map[string]string{
"name": Bold + BgRGB(0, 0, 2) + FgGray(23),
"ext": FgRGB(2, 2, 5),
},
".dir": map[string]string{
"name": Bold + BgRGB(0, 0, 1) + FgGray(23),
"ext": FgRGB(2, 2, 5),
},
"folderHeader": map[string]string{
"arrow": FgRGB(3, 2, 0),
"main": BgGray(2) + FgRGB(3, 2, 0),
"slash": FgGray(5),
"lastFolder": Bold + FgRGB(5, 5, 0),
"error": BgRGB(5, 0, 0) + FgRGB(5, 5, 0),
},
"link": map[string]string{
"name": Bold + FgRGB(0, 5, 0),
"nameDir": Bold + BgRGB(0, 0, 2) + FgRGB(0, 5, 5),
"arrow": FgRGB(1, 0, 1),
"path": FgRGB(4, 0, 4),
"broken": FgRGB(5, 0, 0),
},
"device": map[string]string{
"name": Bold + BgGray(3) + Fg(220),
},
"socket": map[string]string{
"name": Bold + Bg(53) + Fg(15),
},
"pipe": map[string]string{
"name": Bold + Bg(94) + Fg(15),
},
"stats": map[string]string{
"text": BgGray(2) + FgGray(15),
"number": Fg(24),
"ms": Fg(39),
},
}
// PermsColor holds color mappings for users and groups
PermsColor = map[string]map[string]string{
"user": map[string]string{
"root": FgRGB(5, 0, 2),
"daemon": FgRGB(4, 2, 1),
"_self": FgRGB(0, 4, 0),
"_default": FgRGB(0, 3, 3),
},
"group": map[string]string{
"wheel": FgRGB(3, 0, 0),
"staff": FgRGB(0, 2, 0),
"admin": FgRGB(2, 2, 0),
"_default": FgRGB(2, 0, 2),
},
"other": map[string]string{
"_default": BgGray(2) + FgGray(15),
},
}
)

View File

@ -1,402 +0,0 @@
package lsgo
import (
"strings"
)
func getIconForFile(name, ext string) string {
// default icon for all files. try to find a better one though...
icon := icons["file"]
// resolve aliased extensions
extKey := strings.ToLower(ext)
alias, hasAlias := aliases[extKey]
if hasAlias {
extKey = alias
}
// see if we can find a better icon based on extension alone
betterIcon, hasBetterIcon := icons[extKey]
if hasBetterIcon {
icon = betterIcon
}
// now look for icons based on full names
fullName := name
if ext != "" {
fullName += "." + ext
}
fullName = strings.ToLower(fullName)
fullAlias, hasFullAlias := aliases[fullName]
if hasFullAlias {
fullName = fullAlias
}
bestIcon, hasBestIcon := icons[fullName]
if hasBestIcon {
icon = bestIcon
}
return icon
}
func getIconForFolder(name string) string {
icon := folders["folder"]
betterIcon, hasBetterIcon := folders[name]
if hasBetterIcon {
icon = betterIcon
}
return icon
}
var icons = map[string]string{
"ai": "\ue7b4",
"android": "\ue70e",
"apple": "\uf179",
"as": "\ue60b",
"asm": "\ufb19",
"audio": "\uf1c7",
"avro": "\ue60b",
"bf": "\uf067",
"binary": "\uf471",
"c": "\ue61e",
"cargo.lock": "\uf487",
"cargo.toml": "\uf487",
"cfg": "\uf423",
"clj": "\ue768",
"coffee": "\ue751",
"conf": "\ue615",
"cpp": "\ue61d",
"cr": "\ue23e",
"cs": "\uf81a",
"cson": "\ue601",
"css": "\ue749",
"d": "\ue7af",
"dart": "\ue798",
"db": "\uf1c0",
"deb": "\uf306",
"diff": "\uf440",
"doc": "\uf1c2",
"dockerfile": "\ue7b0",
"dpkg": "\uf17c",
"ebook": "\uf02d",
"elm": "\ue62c",
"env": "\uf462",
"erl": "\ue7b1",
"ex": "\ue62d",
"f": "\uf794",
"file": "\uf15b",
"font": "\uf031",
"fs": "\ue7a7",
"gb": "\ue272",
"gform": "\uf298",
"git": "\ue702",
"go": "\ue724",
"graphql": "\ue284",
"groovy": "\ue775",
"gruntfile.js": "\ue74c",
"gulpfile.js": "\ue610",
"gv": "\ue225",
"h": "\uf0fd",
"hs": "\ue777",
"html": "\uf13b",
"ics": "\uf073",
"image": "\uf1c5",
"iml": "\ue7b5",
"ini": "\uf669",
"ino": "\ue255",
"iso": "\uf7c9",
"java": "\ue738",
"jenkinsfile": "\ue767",
"jl": "\ue624",
"js": "\ue781",
"json": "\ue60b",
"jsx": "\ue7ba",
"key": "\uf43d",
"less": "\ue758",
"lock": "\uf720",
"log": "\uf18d",
"lua": "\ue620",
"m": "\ufb27",
"maintainers": "\uf0c0",
"makefile": "\ue20f",
"md": "\uf48a",
"mjs": "\ue718",
"ml": "\ufb26",
"mustache": "\ue60f",
"nc": "\uf7c0",
"npmignore": "\ue71e",
"passwd": "\uf023",
"patch": "\uf440",
"pdf": "\uf1c1",
"php": "\ue608",
"pl": "\ue7a1",
"ppt": "\uf1c4",
"psd": "\ue7b8",
"py": "\ue606",
"r": "\ufcd2",
"rb": "\ue21e",
"rdb": "\ue76d",
"rpm": "\uf17c",
"rs": "\ue7a8",
"rss": "\uf09e",
"rst": "\uf66a",
"rubydoc": "\ue73b",
"sass": "\ue603",
"scala": "\ue737",
"shell": "\uf489",
"shp": "\ufa5f",
"sol": "\ufcb9",
"sql": "\ue706",
"sqlite3": "\ue7c4",
"styl": "\ue600",
"swift": "\ue755",
"tex": "\u222b",
"tfrecord": "\ufb27",
"toml": "\uf669",
"ts": "\ufbe4",
"twig": "\ue61c",
"txt": "\uf15c",
"vagrantfile": "\ue21e",
"video": "\uf03d",
"vim": "\ue62b",
"vue": "\ufd42",
"windows": "\uf17a",
"xls": "\uf1c3",
"xml": "\ue796",
"yml": "\ue601",
"zip": "\uf410",
}
var aliases = map[string]string{
"apk": "android",
"gradle": "android",
"ds_store": "apple",
"localized": "apple",
"s": "asm",
"aac": "audio",
"alac": "audio",
"flac": "audio",
"m4a": "audio",
"mka": "audio",
"mp3": "audio",
"ogg": "audio",
"opus": "audio",
"wav": "audio",
"wma": "audio",
"b": "bf",
"bson": "binary",
"feather": "binary",
"mat": "binary",
"o": "binary",
"pb": "binary",
"pickle": "binary",
"pkl": "binary",
"conf": "cfg",
"config": "cfg",
"cljc": "clj",
"cljs": "clj",
"editorconfig": "conf",
"rc": "conf",
"c++": "cpp",
"cc": "cpp",
"cxx": "cpp",
"scss": "css",
"docx": "doc",
"gdoc": "doc",
"epub": "ebook",
"ipynb": "ebook",
"mobi": "ebook",
"f03": "f",
"f77": "f",
"f90": "f",
"f95": "f",
"for": "f",
"fpp": "f",
"ftn": "f",
"eot": "font",
"otf": "font",
"ttf": "font",
"woff": "font",
"woff2": "font",
"fsi": "fs",
"fsscript": "fs",
"fsx": "fs",
"dna": "gb",
"gitattributes": "git",
"gitconfig": "git",
"gitignore": "git",
"gitignore_global": "git",
"gitmirrorall": "git",
"gitmodules": "git",
"gsh": "groovy",
"gvy": "groovy",
"gy": "groovy",
"h++": "h",
"hh": "h",
"hpp": "h",
"hxx": "h",
"lhs": "hs",
"htm": "html",
"xhtml": "html",
"bmp": "image",
"cbr": "image",
"cbz": "image",
"dvi": "image",
"eps": "image",
"gif": "image",
"ico": "image",
"jpeg": "image",
"jpg": "image",
"nef": "image",
"orf": "image",
"pbm": "image",
"pgm": "image",
"png": "image",
"pnm": "image",
"ppm": "image",
"pxm": "image",
"stl": "image",
"svg": "image",
"tif": "image",
"tiff": "image",
"webp": "image",
"xpm": "image",
"disk": "iso",
"dmg": "iso",
"smi": "iso",
"img": "iso",
"vhd": "iso",
"vhdx": "iso",
"vmdk": "iso",
"jar": "java",
"properties": "json",
"webmanifest": "json",
"tsx": "jsx",
"cjsx": "jsx",
"cer": "key",
"crt": "key",
"der": "key",
"gpg": "key",
"p7b": "key",
"pem": "key",
"pfx": "key",
"pgp": "key",
"license": "key",
"codeowners": "maintainers",
"credits": "maintainers",
"cmake": "makefile",
"markdown": "md",
"mkd": "md",
"rdoc": "md",
"readme": "md",
"mli": "ml",
"sml": "ml",
"netcdf": "nc",
"php3": "php",
"php4": "php",
"php5": "php",
"phpt": "php",
"phtml": "php",
"gslides": "ppt",
"pptx": "ppt",
"pxd": "py",
"pyc": "py",
"pyx": "py",
"whl": "py",
"rdata": "r",
"rds": "r",
"rmd": "r",
"gemfile": "rb",
"gemspec": "rb",
"guardfile": "rb",
"procfile": "rb",
"rakefile": "rb",
"rspec": "rb",
"rspec_parallel": "rb",
"rspec_status": "rb",
"ru": "rb",
"erb": "rubydoc",
"slim": "rubydoc",
"awk": "shell",
"bash": "shell",
"bash_history": "shell",
"bash_profile": "shell",
"bashrc": "shell",
"csh": "shell",
"fish": "shell",
"ksh": "shell",
"sh": "shell",
"zsh": "shell",
"zsh-theme": "shell",
"zshrc": "shell",
"plpgsql": "sql",
"plsql": "sql",
"psql": "sql",
"tsql": "sql",
"sl3": "sqlite3",
"stylus": "styl",
"cls": "tex",
"avi": "video",
"flv": "video",
"m2v": "video",
"mkv": "video",
"mov": "video",
"mp4": "video",
"mpeg": "video",
"mpg": "video",
"ogm": "video",
"ogv": "video",
"vob": "video",
"webm": "video",
"vimrc": "vim",
"bat": "windows",
"cmd": "windows",
"exe": "windows",
"csv": "xls",
"gsheet": "xls",
"xlsx": "xls",
"svelte": "xml",
"plist": "xml",
"xul": "xml",
"yaml": "yml",
"7z": "zip",
"Z": "zip",
"bz2": "zip",
"gz": "zip",
"lzma": "zip",
"par": "zip",
"rar": "zip",
"tar": "zip",
"tc": "zip",
"tgz": "zip",
"txz": "zip",
"xz": "zip",
"z": "zip",
}
var folders = map[string]string{
".atom": "\ue764",
".aws": "\ue7ad",
".docker": "\ue7b0",
".gem": "\ue21e",
".git": "\ue5fb",
".git-credential-cache": "\ue5fb",
".github": "\ue5fd",
".npm": "\ue5fa",
".nvm": "\ue718",
".rvm": "\ue21e",
".Trash": "\uf1f8",
".vscode": "\ue70c",
".vim": "\ue62b",
"config": "\ue5fc",
"folder": "\uf07c",
"hidden": "\uf023",
"node_modules": "\ue5fa",
}
var otherIcons = map[string]string{
"link": "\uf0c1",
"linkDir": "\uf0c1",
"brokenLink": "\uf127",
"device": "\uf0a0",
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

View File

@ -1,46 +0,0 @@
// +build !windows
package lsgo
import (
"fmt"
"os"
"os/user"
"strconv"
"strings"
"syscall"
"github.com/willf/pad"
"golang.org/x/sys/unix"
)
func getOwnerAndGroup(fileInfo *os.FileInfo) (string, string) {
statT := (*fileInfo).Sys().(*syscall.Stat_t)
uid := fmt.Sprint(statT.Uid)
gid := fmt.Sprint(statT.Gid)
owner, err := user.LookupId(uid)
var ownerName string
if err == nil {
ownerName = owner.Username
} else {
ownerName = uid
}
group, err := user.LookupGroupId(gid)
var groupName string
if err == nil {
groupName = group.Name
} else {
groupName = gid
}
return ownerName, groupName
}
func deviceNumbers(absPath string) string {
stat := syscall.Stat_t{}
err := syscall.Stat(absPath, &stat)
check(err)
major := strconv.FormatInt(int64(unix.Major(uint64(stat.Rdev))), 10)
minor := strconv.FormatInt(int64(unix.Minor(uint64(stat.Rdev))), 10)
return pad.Left(strings.Join([]string{major, minor}, ","), 7, " ") + " " + Reset
}

View File

@ -1,65 +0,0 @@
// +build windows
package lsgo
import (
"os"
"strconv"
"strings"
"syscall"
"unsafe"
"github.com/willf/pad"
"golang.org/x/sys/windows"
)
var (
libadvapi32 = syscall.NewLazyDLL("advapi32.dll")
procGetFileSecurity = libadvapi32.NewProc("GetFileSecurityW")
procGetSecurityDescriptorOwner = libadvapi32.NewProc("GetSecurityDescriptorOwner")
)
func getOwnerAndGroup(fileInfo *os.FileInfo) (string, string) {
path := (*fileInfo).Name()
var needed uint32
procGetFileSecurity.Call(
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))),
0x00000001, /* OWNER_SECURITY_INFORMATION */
0,
0,
uintptr(unsafe.Pointer(&needed)))
buf := make([]byte, needed)
r1, _, err := procGetFileSecurity.Call(
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))),
0x00000001, /* OWNER_SECURITY_INFORMATION */
uintptr(unsafe.Pointer(&buf[0])),
uintptr(needed),
uintptr(unsafe.Pointer(&needed)))
if r1 == 0 && err != nil {
return "", ""
}
var ownerDefaulted uint32
var sid *syscall.SID
r1, _, err = procGetSecurityDescriptorOwner.Call(
uintptr(unsafe.Pointer(&buf[0])),
uintptr(unsafe.Pointer(&sid)),
uintptr(unsafe.Pointer(&ownerDefaulted)))
if r1 == 0 && err != nil {
return "", ""
}
uid, gid, _, err := sid.LookupAccount("")
if r1 == 0 && err != nil {
return "", ""
}
return uid, gid
}
func deviceNumbers(absPath string) string {
stat := syscall.Stat_t{}
err := syscall.Stat(absPath, &stat)
check(err)
major := strconv.FormatInt(int64(windows.Major(uint64(stat.Rdev))), 10)
minor := strconv.FormatInt(int64(windows.Minor(uint64(stat.Rdev))), 10)
return pad.Left(strings.Join([]string{major, minor}, ","), 7, " ") + " " + Reset
}

View File

@ -1,650 +0,0 @@
package lsgo
import (
"fmt"
"io/ioutil"
"math"
"os"
"path"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"time"
"github.com/acarl005/textcol"
colorable "github.com/mattn/go-colorable"
"github.com/willf/pad"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
// DisplayItem wraps the file stat info and string to be printed
type DisplayItem struct {
display string
info os.FileInfo
basename string
ext string
link *LinkInfo
}
// LinkInfo wraps link stat info and whether the link points to valid file
type LinkInfo struct {
path string
info os.FileInfo
broken bool
}
var (
// True is a helper varable to help make pointers to `true`
True = true
sizeUnits = []string{"B", "K", "M", "G", "T"}
dateFormat = "02.Jan'06" // uses the "reference time" https://golang.org/pkg/time/#Time.Format
timeFormat = "15:04"
start int64 // keep track of execution time
stdout = colorable.NewColorableStdout() // write to this to allow ANSI color codes to be compatible on Windows
)
// func LsGo(cmd *cobra.Command, args []string) {
func LsGo() error {
var err error
textcol.Output = stdout
start = time.Now().UnixNano()
// auto-generate help text for the command with -h
kingpin.CommandLine.HelpFlag.Short('h')
// parse the arguments and populate the struct
kingpin.Parse()
argsPostParse()
// separate the directories from the regular files
dirs := []string{}
files := []os.FileInfo{}
for _, pathStr := range *args.paths {
var fileStat os.FileInfo
fileStat, err = os.Stat(pathStr)
if err != nil && strings.Contains(err.Error(), "no such file or directory") {
printErrorHeader(err, prettifyPath(pathStr))
continue
} else {
check(err)
}
if fileStat.IsDir() {
dirs = append(dirs, pathStr)
} else {
files = append(files, fileStat)
}
}
// list files first
if len(files) > 0 {
pwd := os.Getenv("PWD")
listFiles(pwd, &files, true)
}
// then list the contents of each directory
for i, dir := range dirs {
// print a blank line between directories, but not before the first one
if i > 0 {
fmt.Fprintln(stdout, "")
}
listDir(dir)
}
return err
}
func listDir(pathStr string) {
items, err := ioutil.ReadDir(pathStr)
// if we couldn't read the folder, print a "header" with error message and use error-looking colors
if err != nil {
if strings.Contains(err.Error(), "no such file or directory") || strings.Contains(err.Error(), "permission denied") {
printErrorHeader(err, prettifyPath(pathStr))
return
}
check(err)
}
// filter by the regexp if one was passed
if len(*args.find) > 0 {
filteredItems := []os.FileInfo{}
for _, fileInfo := range items {
re, err := regexp.Compile(*args.find)
check(err)
if re.MatchString(fileInfo.Name()) {
filteredItems = append(filteredItems, fileInfo)
}
}
items = filteredItems
}
if !(len(*args.find) > 0 && len(items) == 0) &&
!(len(*args.paths) == 1 && (*args.paths)[0] == "." && !*args.recurse) {
printFolderHeader(pathStr)
}
if len(items) > 0 {
listFiles(pathStr, &items, false)
}
if *args.recurse {
for _, item := range items {
if item.IsDir() && (item.Name()[0] != '.' || *args.all) {
fmt.Fprintln(stdout, "") // put a blank line between directories
listDir(path.Join(pathStr, item.Name()))
}
}
}
}
func listFiles(parentDir string, items *[]os.FileInfo, forceDotfiles bool) {
absPath, err := filepath.Abs(parentDir)
check(err)
// collect all the contents here
files := []*DisplayItem{}
dirs := []*DisplayItem{}
// to help with formatting, we need to know the length of the longest name to add appropriate padding
longestOwnerName := 0
longestGroupName := 0
if *args.owner {
for _, fileInfo := range *items {
owner, group := getOwnerAndGroup(&fileInfo)
longestOwnerName = max(longestOwnerName, len(owner))
longestGroupName = max(longestGroupName, len(group))
}
}
for _, fileInfo := range *items {
// if this is a dotfile (hidden file)
if fileInfo.Name()[0] == '.' {
// we can skip everything with this file if we aren't using the `all` option
if !*args.all && !forceDotfiles {
continue
}
}
basename, ext := splitExt(fileInfo.Name())
displayItem := DisplayItem{
info: fileInfo,
ext: ext,
basename: basename,
}
// read some info about linked file if this item is a symlink
if fileInfo.Mode()&os.ModeSymlink != 0 {
getLinkInfo(&displayItem, absPath)
}
if fileInfo.IsDir() || (fileInfo.Mode()&os.ModeSymlink != 0 &&
displayItem.link.info != nil &&
displayItem.link.info.IsDir()) {
if *args.files {
continue
} else {
dirs = append(dirs, &displayItem)
}
} else {
if *args.dirs {
continue
} else {
files = append(files, &displayItem)
}
}
owner, group := getOwnerAndGroup(&fileInfo)
ownerColor, groupColor := getOwnerAndGroupColors(owner, group)
if *args.perms {
displayItem.display += permString(fileInfo, ownerColor, groupColor)
}
if *args.owner {
paddedOwner := pad.Right(owner, longestOwnerName, " ")
ownerInfo := []string{Reset + ownerColor + paddedOwner}
if !*args.nogroup {
paddedGroup := pad.Right(group, longestGroupName, " ")
ownerInfo = append(ownerInfo, groupColor+paddedGroup)
}
ownerInfo = append(ownerInfo, Reset)
displayItem.display += strings.Join(ownerInfo, " ")
}
if *args.bytes {
if fileInfo.Mode()&os.ModeDevice != 0 {
displayItem.display += deviceNumbers(path.Join(absPath, fileInfo.Name()))
} else {
displayItem.display += sizeString(fileInfo.Size())
}
}
if *args.mdate {
displayItem.display += timeString(fileInfo.ModTime())
}
displayItem.display += nameString(&displayItem)
if *args.links && fileInfo.Mode()&os.ModeSymlink != 0 {
displayItem.display += linkString(&displayItem, absPath)
}
}
if *args.sortTime {
sort.Sort(ByTime(dirs))
sort.Sort(ByTime(files))
if *args.backwards {
reverse(dirs)
reverse(files)
}
}
if *args.sortSize {
sort.Sort(BySize(files))
if *args.backwards {
reverse(files)
}
}
if *args.sortKind {
sort.Sort(ByKind(files))
if *args.backwards {
reverse(files)
}
}
// combine the items together again after sorting
allItems := append(dirs, files...)
// if using "long" display, just print one item per line
if *args.bytes || *args.mdate || *args.owner || *args.perms || *args.long {
for _, item := range allItems {
fmt.Fprintln(stdout, item.display)
}
} else {
// but if not, try to format in columns, link `ls` would
strs := []string{}
for _, item := range allItems {
strs = append(strs, item.display)
}
textcol.PrintColumns(&strs, 2)
}
if *args.stats {
printStats(len(files), len(dirs))
}
}
func getLinkInfo(item *DisplayItem, absPath string) {
fullPath := path.Join(absPath, item.info.Name())
linkPath, err1 := os.Readlink(fullPath)
check(err1)
linkFullPath := linkPath
if linkPath[0] != '/' {
linkFullPath = path.Join(absPath, linkPath)
}
linkInfo, err2 := os.Stat(linkFullPath)
if *args.linkRel {
linkRel, _ := filepath.Rel(absPath, linkPath)
if linkRel != "" && len(linkRel) <= len(linkPath) {
// i prefer the look of these relative paths prepended with ./
if linkRel[0] != '.' {
linkPath = "./" + linkRel
} else {
linkPath = linkRel
}
}
}
link := LinkInfo{
path: linkPath,
}
item.link = &link
if linkInfo != nil {
link.info = linkInfo
} else if strings.Contains(err2.Error(), "no such file or directory") {
link.broken = true
} else if !strings.Contains(err2.Error(), "permission denied") {
check(err2)
}
}
func nameString(item *DisplayItem) string {
mode := item.info.Mode()
name := item.info.Name()
if mode&os.ModeDir != 0 {
return dirString(item)
} else if mode&os.ModeSymlink != 0 {
if !item.link.broken && item.link.info.IsDir() {
color := ConfigColor["link"]["nameDir"]
if *args.nerdfont {
var linkIcon string
if item.link.broken {
linkIcon = otherIcons["brokenLink"]
} else {
linkIcon = otherIcons["linkDir"]
}
return color + linkIcon + " " + name + " " + Reset
} else if *args.icons {
return color + "🔗 " + name + " " + Reset
} else {
return color + " " + name + " " + Reset
}
} else {
color := ConfigColor["link"]["name"]
if *args.nerdfont {
var linkIcon string
if item.link.broken {
linkIcon = otherIcons["brokenLink"]
} else {
linkIcon = otherIcons["link"]
}
return color + linkIcon + " " + name + " " + Reset
} else if *args.icons {
return color + "🔗 " + name + " " + Reset
} else {
return color + name + " " + Reset
}
}
} else if mode&os.ModeDevice != 0 {
color := ConfigColor["device"]["name"]
if *args.nerdfont {
return color + otherIcons["device"] + " " + name + " " + Reset
} else if *args.icons {
return color + "💽 " + name + " " + Reset
} else {
return color + " " + name + " " + Reset
}
} else if mode&os.ModeNamedPipe != 0 {
return ConfigColor["pipe"]["name"] + " " + name + " " + Reset
} else if mode&os.ModeSocket != 0 {
return ConfigColor["socket"]["name"] + " " + name + " " + Reset
}
return fileString(item)
}
func linkString(item *DisplayItem, absPath string) string {
colors := ConfigColor["link"]
displayStrings := []string{colors["arrow"] + "►"}
if item.link.info == nil && item.link.broken {
displayStrings = append(displayStrings, colors["broken"]+item.link.path+Reset)
} else if item.link.info != nil {
linkname, linkext := splitExt(item.link.path)
displayItem := DisplayItem{
info: item.link.info,
basename: linkname,
ext: linkext,
}
displayStrings = append(displayStrings, nameString(&displayItem))
} else {
displayStrings = append(displayStrings, item.link.path)
}
return strings.Join(displayStrings, " ")
}
func fileString(item *DisplayItem) string {
key := strings.ToLower(item.ext)
// figure out which color to choose
colors := FileColor["_default"]
alias, hasAlias := FileAliases[key]
if hasAlias {
key = alias
}
betterColor, hasBetterColor := FileColor[key]
if hasBetterColor {
colors = betterColor
}
ext := item.ext
if ext != "" {
ext = "." + ext
}
// in some cases files have icons if front
// if nerd font enabled, then it'll be a file-specific icon, or if its an executable script, a little shell icon
// if the regular --icons flag is used instead, then it will show a ">_" only if the file is executable
icon := ""
executable := isExecutableScript(item)
if *args.nerdfont {
if executable {
icon = colors[0] + getIconForFile("", "shell") + " "
} else {
icon = colors[0] + getIconForFile(item.basename, item.ext) + " "
}
} else if *args.icons {
if executable {
icon = BgGray(1) + FgRGB(0, 5, 0) + ">_" + Reset + " "
}
}
displayStrings := []string{icon, colors[0], item.basename, colors[1], ext, Reset}
return strings.Join(displayStrings, "")
}
// check for executable permissions
func isExecutableScript(item *DisplayItem) bool {
if item.info.Mode()&0111 != 0 && item.info.Mode().IsRegular() {
return true
}
return false
}
func dirString(item *DisplayItem) string {
colors := ConfigColor["dir"]
if item.basename == "" {
colors = ConfigColor[".dir"]
}
displayStrings := []string{colors["name"]}
icon := ""
if *args.icons {
displayStrings = append(displayStrings, "📂 ")
} else if *args.nerdfont {
icon = getIconForFolder(item.info.Name()) + " "
displayStrings = append(displayStrings, icon)
} else {
displayStrings = append(displayStrings, " ")
}
ext := item.ext
if ext != "" {
ext = "." + ext
}
displayStrings = append(displayStrings, item.basename, colors["ext"], ext, " ", Reset)
return strings.Join(displayStrings, "")
}
func rwxString(mode os.FileMode, i uint, color string) string {
bits := mode >> (i * 3)
coloredStrings := []string{color}
if bits&4 != 0 {
coloredStrings = append(coloredStrings, "r")
} else {
coloredStrings = append(coloredStrings, "-")
}
if bits&2 != 0 {
coloredStrings = append(coloredStrings, "w")
} else {
coloredStrings = append(coloredStrings, "-")
}
if i == 0 && mode&os.ModeSticky != 0 {
if bits&1 != 0 {
coloredStrings = append(coloredStrings, "t")
} else {
coloredStrings = append(coloredStrings, "T")
}
} else {
if bits&1 != 0 {
coloredStrings = append(coloredStrings, "x")
} else {
coloredStrings = append(coloredStrings, "-")
}
}
return strings.Join(coloredStrings, "")
}
// generates the permissions string, ya know like "drwxr-xr-x" and stuff like that
func permString(info os.FileInfo, ownerColor string, groupColor string) string {
defaultColor := PermsColor["other"]["_default"]
// info.Mode().String() does not produce the same output as `ls`, so we must build that string manually
mode := info.Mode()
// this "type" is not the file extension, but type as far as the OS is concerned
filetype := "-"
if mode&os.ModeDir != 0 {
filetype = "d"
} else if mode&os.ModeSymlink != 0 {
filetype = "l"
} else if mode&os.ModeDevice != 0 {
if mode&os.ModeCharDevice == 0 {
filetype = "b" // block device
} else {
filetype = "c" // character device
}
} else if mode&os.ModeNamedPipe != 0 {
filetype = "p"
} else if mode&os.ModeSocket != 0 {
filetype = "s"
}
coloredStrings := []string{defaultColor, filetype}
coloredStrings = append(coloredStrings, rwxString(mode, 2, ownerColor))
coloredStrings = append(coloredStrings, rwxString(mode, 1, groupColor))
coloredStrings = append(coloredStrings, rwxString(mode, 0, defaultColor), Reset, Reset)
return strings.Join(coloredStrings, " ")
}
func sizeString(size int64) string {
sizeFloat := float64(size)
for i, unit := range sizeUnits {
base := math.Pow(1024, float64(i))
if sizeFloat < base*1024 {
var sizeStr string
if i == 0 {
sizeStr = strconv.FormatInt(size, 10)
} else {
sizeStr = fmt.Sprintf("%.2f", sizeFloat/base)
}
return SizeColor[unit] + pad.Left(sizeStr, 6, " ") + unit + " " + Reset
}
}
return strconv.Itoa(int(size))
}
func timeString(modtime time.Time) string {
dateStr := modtime.Format(dateFormat)
timeStr := modtime.Format(timeFormat)
hour, err := strconv.Atoi(timeStr[0:2])
check(err)
// generate a color based on the hour of the day. darkest around midnight and whitest around noon
timeColor := 14 - int(8*math.Cos(math.Pi*float64(hour)/12))
colored := []string{FgGray(22) + dateStr, FgGray(timeColor) + timeStr, Reset}
return strings.Join(colored, " ")
}
// when we list out any subdirectories, print those paths conspicuously above the contents
// this helps with visual separation
func printFolderHeader(pathStr string) {
colors := ConfigColor["folderHeader"]
headerString := colors["arrow"] + "►" + colors["main"] + " "
prettyPath := prettifyPath(pathStr)
if prettyPath == "/" {
headerString += "/"
} else {
folders := strings.Split(prettyPath, "/")
coloredFolders := make([]string, 0, len(folders))
for i, folder := range folders {
if i == len(folders)-1 { // different color for the last folder in the path
coloredFolders = append(coloredFolders, colors["lastFolder"]+folder)
} else {
coloredFolders = append(coloredFolders, colors["main"]+folder)
}
}
headerString += strings.Join(coloredFolders, colors["slash"]+"/")
}
fmt.Fprintln(stdout, headerString+" "+Reset)
}
func printErrorHeader(err error, pathStr string) {
fmt.Fprintln(stdout, ConfigColor["folderHeader"]["error"]+"► "+pathStr+Reset)
fmt.Fprintln(stdout, err.Error())
}
func prettifyPath(pathStr string) string {
prettyPath, err := filepath.Abs(pathStr)
check(err)
pwd := os.Getenv("PWD")
home := os.Getenv("HOME")
if strings.HasPrefix(prettyPath, pwd) {
prettyPath = "." + prettyPath[len(pwd):]
} else if strings.HasPrefix(prettyPath, home) {
prettyPath = "~" + prettyPath[len(home):]
}
return prettyPath
}
func getOwnerAndGroupColors(owner string, group string) (string, string) {
if owner == os.Getenv("USER") {
owner = "_self"
}
ownerColor := PermsColor["user"][owner]
if ownerColor == "" {
ownerColor = PermsColor["user"]["_default"]
}
groupColor := PermsColor["group"][group]
if groupColor == "" {
groupColor = PermsColor["group"]["_default"]
}
return ownerColor, groupColor
}
func printStats(numFiles, numDirs int) {
colors := ConfigColor["stats"]
end := time.Now().UnixNano()
microSeconds := (end - start) / int64(time.Microsecond)
milliSeconds := float64(microSeconds) / 1000
statStrings := []string{
colors["text"],
colors["number"] + strconv.Itoa(numDirs),
colors["text"] + "dirs",
colors["number"] + strconv.Itoa(numFiles),
colors["text"] + "files",
colors["ms"] + fmt.Sprintf("%.2f", milliSeconds),
colors["text"] + "ms",
Reset,
}
fmt.Fprintln(stdout, strings.Join(statStrings, " "))
}
func splitExt(filename string) (basepath, ext string) {
basename := filepath.Base(filename)
if basename[0] == '.' {
ext = basename[1:]
basepath = filename[:len(filename)-len(ext)-1]
} else {
ext = filepath.Ext(filename)
basepath = filename[:len(filename)-len(ext)]
if ext != "" {
ext = ext[1:]
}
}
return
}
// Go doesn't provide a `Max` function for ints like it does for floats (wtf?)
func max(a int, b int) int {
if a > b {
return a
}
return b
}
func check(err error) {
if err != nil {
panic(err)
}
}

View File

@ -1,67 +0,0 @@
package lsgo
// BySize tells `sort.Sort` how to sort by file size
type BySize []*DisplayItem
func (s BySize) Less(i, j int) bool {
return s[i].info.Size() < s[j].info.Size()
}
func (s BySize) Len() int {
return len(s)
}
func (s BySize) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// ByTime tells `sort.Sort` how to sort by last modified time
type ByTime []*DisplayItem
func (s ByTime) Less(i, j int) bool {
return s[i].info.ModTime().Unix() < s[j].info.ModTime().Unix()
}
func (s ByTime) Len() int {
return len(s)
}
func (s ByTime) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// ByKind tells `sort.Sort` how to sort by file extension
type ByKind []*DisplayItem
func (s ByKind) Less(i, j int) bool {
var kindi, kindj string
if s[i].basename == "" {
kindi = "."
} else if s[i].ext == "" {
kindi = "0"
} else {
kindi = s[i].ext
}
if s[j].basename == "" {
kindj = "."
} else if s[j].ext == "" {
kindj = "0"
} else {
kindj = s[j].ext
}
if kindi == kindj {
if kindi == "." {
return s[i].ext < s[j].ext
}
return s[i].basename < s[j].basename
}
return kindi < kindj
}
func (s ByKind) Len() int {
return len(s)
}
func (s ByKind) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func reverse(s []*DisplayItem) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}

View File

@ -1,692 +0,0 @@
package mmGit
import (
"GoSungrow/Only"
"bufio"
"context"
"errors"
"fmt"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
"os"
"os/exec"
"os/signal"
"os/user"
"path/filepath"
"strings"
"syscall"
"time"
)
func (z *Git) Connect() error {
for range Only.Once {
if z.IsNotOk() {
break
}
var ok bool
ok, z.Error = IsDirExists(z.RepoDir)
if z.Error != nil {
break
}
if ok {
z.Error = z.Open()
break
}
z.Error = z.Clone()
if z.Error != nil {
break
}
}
return z.Error
}
func (z *Git) Open() error {
for range Only.Once {
if z.IsNotOk() {
break
}
z.repo, z.Error = git.PlainOpen(z.RepoDir)
if z.Error != nil {
break
}
z.worktree, z.Error = z.repo.Worktree()
if z.Error != nil {
break
}
var ref *plumbing.Reference
ref, z.Error = z.repo.Head()
if z.Error != nil {
break
}
if ref.Hash().IsZero() {
z.Error = errors.New("invalid HEAD reference")
break
}
fmt.Printf("Git opened\n\trepo: %s\n\tdir: %s\n", z.RepoUrl, z.RepoDir)
}
return z.Error
}
func (z *Git) Clone() error {
for range Only.Once {
if z.IsNotOk() {
break
}
var ok bool
ok, z.Error = IsDirExists(z.RepoDir)
if z.Error != nil {
break
}
if ok {
z.Error = errors.New(fmt.Sprintf("Cannot clone - directory '%s' already exists.", z.RepoDir))
break
}
// CONTEXT: Provide Ctrl-C capability as well as operation timeouts.
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
<-stop
fmt.Println("\nCanceling operation...")
cancel()
}()
// CONTEXT: Provide Ctrl-C capability as well as operation timeouts.
pk := z.GetSshAuth()
if z.Error != nil {
break
}
options := &git.CloneOptions {
URL: z.RepoUrl,
Auth: pk,
RemoteName: "",
ReferenceName: "",
SingleBranch: false,
NoCheckout: false,
Depth: 0,
RecurseSubmodules: 0,
Progress: os.Stdout,
Tags: 0,
InsecureSkipTLS: false,
CABundle: nil,
}
z.repo, z.Error = git.PlainCloneContext(ctx, z.RepoDir, false, options)
if z.Error != nil {
break
}
z.worktree, z.Error = z.repo.Worktree()
if z.Error != nil {
break
}
var ref *plumbing.Reference
ref, z.Error = z.repo.Head()
if z.Error != nil {
break
}
if ref.Hash().IsZero() {
z.Error = errors.New("invalid HEAD reference")
break
}
fmt.Printf("Git cloned\n\trepo: %s\n\tdir: %s\n", z.RepoUrl, z.RepoDir)
}
return z.Error
}
// GetSshAuth: Gitlab keys need to be created with at least 3072 bits.
// ssh-keygen -t rsa -b 3072 -C 'root@everywhere' -f gitlab_rsa -N ''
func (z *Git) GetSshAuth() *ssh.PublicKeys {
var pk *ssh.PublicKeys
for range Only.Once {
if z.IsNotOk() {
break
}
var u *user.User
u, z.Error = user.Current()
paths := []string {
z.KeyFile,
filepath.Join(u.HomeDir, ".ssh", "id_rsa"),
}
var path string
for _, path = range paths {
if path == "" {
continue
}
z.Error = checkKeyFile(path)
if z.Error != nil {
continue
}
break
}
// Try without password first.
var password string
pk, z.Error = ssh.NewPublicKeysFromFile("git", path, password)
if z.Error == nil {
fmt.Printf("AUTH: %v\n", pk)
break
}
// Then with password.
password = getPassword("ApiPassword: ")
pk, z.Error = ssh.NewPublicKeysFromFile("git", path, password)
if z.Error == nil {
fmt.Printf("AUTH: %v\n", pk)
break
}
}
return pk
}
func checkKeyFile(path string) error {
var err error
for range Only.Once {
if path == "" {
continue
}
var fi os.FileInfo
fi, err = os.Stat(path)
if os.IsNotExist(err) {
continue
}
if fi.IsDir() {
err = errors.New("SSH publickey file is a directory")
continue
}
}
return err
}
// techEcho() - turns terminal echo on or off.
func termEcho(on bool) {
// Common settings and variables for both stty calls.
attrs := syscall.ProcAttr{
Dir: "",
Env: []string{},
Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
Sys: nil}
var ws syscall.WaitStatus
cmd := "echo"
if on == false {
cmd = "-echo"
}
// Enable/disable echoing.
pid, err := syscall.ForkExec(
"/bin/stty",
[]string{"stty", cmd},
&attrs)
if err != nil {
panic(err)
}
// Wait for the stty process to complete.
_, err = syscall.Wait4(pid, &ws, 0, nil)
if err != nil {
panic(err)
}
}
// getPassword - Prompt for password.
func getPassword(prompt string) string {
fmt.Print(prompt)
// Catch a ^C interrupt.
// Make sure that we reset term echo before exiting.
signalChannel := make(chan os.Signal, 1)
signal.Notify(signalChannel, os.Interrupt)
go func() {
for _ = range signalChannel {
fmt.Println("\n^C interrupt.")
termEcho(true)
os.Exit(1)
}
}()
// Echo is disabled, now grab the data.
termEcho(false) // disable terminal echo
reader := bufio.NewReader(os.Stdin)
text, err := reader.ReadString('\n')
termEcho(true) // always re-enable terminal echo
fmt.Println("")
if err != nil {
// The terminal has been reset, go ahead and exit.
fmt.Println("ERROR:", err.Error())
os.Exit(1)
}
return strings.TrimSpace(text)
}
//func (z *Git) setContext() error {
//
// for range Only.Once {
// if z.IsNotOk() {
// break
// }
//
// stop := make(chan os.Signal, 1)
// signal.Notify(stop, os.Interrupt)
// ctx, cancel := context.WithCancel(context.Background())
// defer cancel() // cancel when we are finished consuming integers
//
// go func() {
// <-stop
// Warning("\nSignal detected, canceling operation...")
// cancel()
// }()
//
// var auth ssh.AuthMethod
// auth, z.Error = ssh.DefaultAuthBuilder("admin-mickh")
// if z.Error != nil {
// break
// }
//
// z.repo, z.Error = git.PlainClone(z.RepoDir, false, &git.CloneOptions {
// URL: z.RepoUrl,
// Auth: auth,
// })
// if z.Error != nil {
// break
// }
//
// var ref *plumbing.Reference
// ref, z.Error = z.repo.Head()
// if z.Error != nil {
// break
// }
//
// var commit *object.Commit
// commit, z.Error = z.repo.CommitObject(ref.Hash())
// if z.Error != nil {
// break
// }
//
// fmt.Println(commit)
// }
//
// return z.Error
//}
func (z *Git) SaveFile(fn string, data []byte) error {
for range Only.Once {
if z.IsNotOk() {
break
}
//z.worktree, z.Error = z.repo.Worktree()
//if z.Error != nil {
// break
//}
//
//var fh fs.File
//var fi os.FileInfo
//fi, z.Error = z.fs.Stat(fn)
//if errors.Is(z.Error, os.ErrNotExist) {
// // Create new file
// fh, z.Error = z.fs.Create(fn)
//} else {
// // Open file
// fh, z.Error = z.fs.OpenFile(fn, os.O_RDWR|os.O_CREATE, 0664)
//}
fh, err := os.OpenFile(filepath.Join(z.RepoDir, fn), os.O_RDWR|os.O_CREATE, 0664)
if err != nil {
z.Error = err
break
}
defer fh.Close()
fmt.Printf("Saved file '%s'\n", fn)
_, z.Error = fh.Write(data)
if z.Error != nil {
break
}
// Run git status before adding the file to the worktree
//fmt.Println(z.worktree.Status())
// git add $filePath
_, z.Error = z.worktree.Add(fn)
if z.Error != nil {
break
}
//// Run git status after the file has been added adding to the worktree
//fmt.Println(z.worktree.Status())
//
//// git commit -m $message
//msg := fmt.Sprintf("Updated file '%s'", fn)
//_, z.Error = z.worktree.Commit(msg, &git.CommitOptions{})
//if z.Error != nil {
// break
//}
}
return z.Error
}
func (z *Git) Status() error {
for range Only.Once {
if z.IsNotOk() {
break
}
var status git.Status
status, z.Error = z.worktree.Status()
if z.Error != nil {
break
}
if status.String() != "" {
fmt.Printf("Status of Git\n\trepo: %s\n\tdir: %s\n%s\n",
z.RepoUrl,
z.RepoDir,
status.String(),
)
}
}
return z.Error
}
func (z *Git) Add(path string) error {
for range Only.Once {
if z.IsNotOk() {
break
}
if path == "" {
path = "."
}
fmt.Printf("Adding to Git\n\trepo: %s\n\tdir: %s\n", z.RepoUrl, z.RepoDir)
_, z.Error = z.worktree.Add(path)
if z.Error != nil {
break
}
z.Error = z.Status()
if z.Error != nil {
break
}
}
//PrintError(z.Error)
return z.Error
}
func (z *Git) Commit(msg string, args ...interface{}) error {
for range Only.Once {
if z.IsNotOk() {
break
}
z.Error = z.Add(".")
if z.Error != nil {
break
}
cn := &object.Signature {
Name: os.Getenv("USERNAME"),
Email: "",
When: time.Now(),
}
fmt.Printf("Committing Git\n\trepo: %s\n\tdir: %s\n", z.RepoUrl, z.RepoDir)
// Similar to git commit -m $message
var ph plumbing.Hash
msg := fmt.Sprintf(msg, args...)
ph, z.Error = z.worktree.Commit(msg, &git.CommitOptions{
All: false,
Author: cn,
Committer: cn,
Parents: nil,
SignKey: nil,
})
if z.Error != nil {
break
}
// Similar to git show -s
var obj *object.Commit
obj, z.Error = z.repo.CommitObject(ph)
if z.Error != nil {
break
}
if obj.String() != "" {
fmt.Printf("Status of Git\n\trepo: %s\n\tdir: %s\n%s\n",
z.RepoUrl,
z.RepoDir,
obj.String(),
)
}
}
//PrintError(z.Error)
return z.Error
}
func (z *Git) Pull() error {
for range Only.Once {
if z.IsNotOk() {
break
}
pk := z.GetSshAuth()
if z.Error != nil {
break
}
fmt.Printf("Pulling Git\n\trepo: %s\n\tdir: %s\n", z.RepoUrl, z.RepoDir)
z.Error = z.worktree.Pull(&git.PullOptions {
RemoteName: "",
ReferenceName: "",
SingleBranch: false,
Depth: 0,
Auth: pk,
RecurseSubmodules: 0,
Progress: os.Stdout,
Force: false,
InsecureSkipTLS: false,
CABundle: nil,
})
if z.Error.Error() == "already up-to-date" {
z.Error = nil
break
}
if z.Error != nil {
break
}
}
//PrintError(z.Error)
return z.Error
}
func (z *Git) Push() error {
for range Only.Once {
if z.IsNotOk() {
break
}
z.Error = z.Commit("Updated")
if z.Error != nil {
break
}
pk := z.GetSshAuth()
if z.Error != nil {
break
}
fmt.Printf("Pushing Git\n\trepo: %s\n\tdir: %s\n", z.RepoUrl, z.RepoDir)
z.Error = z.repo.Push(&git.PushOptions{
RemoteName: "",
RefSpecs: nil,
Auth: pk,
Progress: os.Stdout,
Prune: false,
Force: false,
InsecureSkipTLS: false,
CABundle: nil,
RequireRemoteRefs: nil,
})
if z.Error != nil {
break
}
}
//PrintError(z.Error)
return z.Error
}
func (z *Git) Diff(path string) error {
for range Only.Once {
var c []CommitDiffs
c, z.Error = z.GetDiffs(path)
if z.Error != nil {
break
}
if len(c) < 2 {
fmt.Printf("Not enough revisions to compare.\n")
break
}
f1 := fmt.Sprintf("%s-%s", path, c[0].Hash)
f1, z.Error = WriteTempFile(f1, c[0].Contents)
if z.Error != nil {
break
}
f2 := fmt.Sprintf("%s-%s", path, c[1].Hash)
f2, z.Error = WriteTempFile(f2, c[1].Contents)
if z.Error != nil {
break
}
if z.DiffCmd == "" {
z.DiffCmd = "tkdiff"
}
z.DiffCmd, z.Error = exec.LookPath(z.DiffCmd)
cmd := exec.Command(z.DiffCmd, f1, f2)
var out []byte
out, z.Error = cmd.Output()
//if z.Error != nil {
// break
//}
fmt.Printf("# %s\n", cmd.String())
fmt.Println(string(out))
if z.Error != nil {
break
}
}
//PrintError(z.Error)
return z.Error
}
type CommitDiffs struct {
Hash string
Contents string
}
func (z *Git) GetDiffs(path string) ([]CommitDiffs, error) {
var ret []CommitDiffs
for range Only.Once {
if z.IsNotOk() {
break
}
fmt.Printf("Diff Git\n\trepo: %s\n\tdir: %s\n", z.RepoUrl, z.RepoDir)
ref, _ := z.repo.Head()
//fmt.Printf("ref '%s'\n", ref.String())
commit, _ := z.repo.CommitObject(ref.Hash())
//fmt.Printf("commit '%s'\n", commit.String())
var comm []*object.Commit
commitIter, _ := z.repo.Log(&git.LogOptions{From: commit.Hash})
_ = commitIter.ForEach(func(c *object.Commit) error {
comm = append(comm, c)
return nil
})
var lastHash string
//for k := 0; k < len(comm)-1; k++ {
for k, _ := range comm {
fmt.Printf("# Commit number[%d]: %s", k, comm[k].Hash)
f2, _ := comm[k].File(path)
if f2 == nil {
fmt.Println(" - no path")
continue
}
fc, _ := f2.Contents()
hs := GetHash(fc)
if hs == lastHash {
fmt.Println(" - no change")
continue
}
lastHash = hs
ret = append(ret, CommitDiffs{
Hash: comm[k].Hash.String(),
Contents: fc,
})
fmt.Println(" - changed")
}
}
return ret, z.Error
}

View File

@ -1,137 +0,0 @@
package mmGit
import (
"GoSungrow/Only"
"fmt"
memfs "github.com/go-git/go-billy/v5/memfs"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
memory "github.com/go-git/go-git/v5/storage/memory"
"os"
//"net/http"
)
func (z *Git) MemConnect() error {
for range Only.Once {
if z.IsNotOk() {
break
}
//auth := &http.BasicAuth {
// ApiUsername: z.ApiUsername,
// ApiPassword: z.ApiPassword,
//}
var auth ssh.AuthMethod
auth, z.Error = ssh.DefaultAuthBuilder("admin-mickh")
if z.Error != nil {
break
}
//if z.Error = r.Push(&git.PushOptions{Auth: sshAuth}); err != nil {
// log.Error().Err(err).Msg("Push err")
// os.Exit(1)
//}
//if z.Error = r.Push(&git.PushOptions{Auth: sshAuth}); err != nil {
// log.Error().Err(err).Msg("Push err")
// os.Exit(1)
//}
//s := fmt.Sprintf("%s/.ssh/id_rsa", os.Getenv("HOME"))
//sshKey, err = ioutil.FileRead(s)
//signer, err := ssh.ParsePrivateKey([]byte(sshKey))
//auth = &gitssh.PublicKeys{User: "git", Signer: signer}
z.storer = memory.NewStorage()
z.fs = memfs.New()
z.repo, z.Error = git.Clone(z.storer, z.fs, &git.CloneOptions{
URL: z.RepoUrl,
Auth: auth,
})
if z.Error != nil {
break
}
}
return z.Error
}
func (z *Git) MemSaveFile(fn string, data []byte) error {
for range Only.Once {
if z.IsNotOk() {
break
}
z.worktree, z.Error = z.repo.Worktree()
if z.Error != nil {
break
}
//var fh fs.File
//var fi os.FileInfo
//fi, z.Error = z.fs.Stat(fn)
//if errors.Is(z.Error, os.ErrNotExist) {
// // Create new file
// fh, z.Error = z.fs.Create(fn)
//} else {
// // Open file
// fh, z.Error = z.fs.OpenFile(fn, os.O_RDWR|os.O_CREATE, 0664)
//}
fh, err := z.fs.OpenFile(fn, os.O_RDWR|os.O_CREATE, 0664)
if err != nil {
z.Error = err
break
}
defer fh.Close()
_, z.Error = fh.Write(data)
if z.Error != nil {
break
}
// Run git status before adding the file to the worktree
fmt.Println(z.worktree.Status())
// git add $filePath
_, z.Error = z.worktree.Add(fn)
if z.Error != nil {
break
}
// Run git status after the file has been added adding to the worktree
fmt.Println(z.worktree.Status())
// git commit -m $message
msg := fmt.Sprintf("Updated file '%s'", fn)
_, z.Error = z.worktree.Commit(msg, &git.CommitOptions{})
if z.Error != nil {
break
}
}
return z.Error
}
func (z *Git) MemCommit(msg string, args ...interface{}) error {
for range Only.Once {
if z.IsNotOk() {
break
}
// Similar to git commit -m $message
msg := fmt.Sprintf(msg, args...)
_, z.Error = z.worktree.Commit(msg, &git.CommitOptions{})
if z.Error != nil {
break
}
}
return z.Error
}

View File

@ -1,159 +0,0 @@
package mmGit
import (
"GoSungrow/Only"
"errors"
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/storage/memory"
)
type Git struct {
Username string
Password string
KeyFile string
Token string
RepoUrl string
RepoDir string
DiffCmd string
repo *git.Repository
modeMemory bool
worktree *git.Worktree
storer *memory.Storage
fs billy.Filesystem
Error error
}
func New() *Git {
var ret Git
for range Only.Once {
}
return &ret
}
func (z *Git) SetAuth(username string, password string) error {
for range Only.Once {
if username == "" {
z.Error = errors.New("username empty")
break
}
z.Username = username
if password == "" {
z.Error = errors.New("password empty")
break
}
z.Password = password
}
return z.Error
}
func (z *Git) SetKeyFile(path string) error {
for range Only.Once {
if path == "" {
break
}
z.Error = checkKeyFile(path)
if z.Error != nil {
break
}
z.KeyFile = path
}
return z.Error
}
func (z *Git) SetToken(t string) error {
for range Only.Once {
if t == "" {
break
}
z.Token = t
}
return z.Error
}
func (z *Git) SetRepo(repo string) error {
for range Only.Once {
if repo == "" {
z.Error = errors.New("repo empty")
break
}
z.RepoUrl = repo
}
return z.Error
}
func (z *Git) SetDir(dir string) error {
for range Only.Once {
if dir == "" {
z.Error = errors.New("dir empty")
break
}
z.RepoDir = dir
}
return z.Error
}
func (z *Git) SetDiffCmd(cmd string) error {
for range Only.Once {
if cmd == "" {
cmd = "tkdiff"
}
z.DiffCmd = cmd
}
return z.Error
}
func (z *Git) IsOk() bool {
var ok bool
for range Only.Once {
//if z.ApiUsername == "" {
// z.Error = errors.New("username empty")
// break
//}
//
//if z.ApiPassword == "" {
// z.Error = errors.New("password empty")
// break
//}
if z.RepoUrl == "" {
z.Error = errors.New("repo empty")
break
}
if z.RepoDir == "" {
z.Error = errors.New("repo dir empty")
break
}
ok = true
}
return ok
}
func (z *Git) IsNotOk() bool {
return !z.IsOk()
}

View File

@ -1,87 +0,0 @@
package mmGit
import (
"GoSungrow/Only"
"crypto/md5"
"encoding/hex"
"errors"
"fmt"
"os"
)
func IsDirExists(path string) (bool, error) {
var ok bool
var err error
for range Only.Once {
if path == "" {
err = errors.New("empty git dir path")
break
}
var fi os.FileInfo
fi, err = os.Stat(path)
if os.IsNotExist(err) {
err = nil
break
}
if !fi.IsDir() {
err = errors.New("git path is not a dir")
break
}
ok = true
}
return ok, err
}
func PrintError(err error) {
if err == nil {
fmt.Println("OK")
return
}
fmt.Printf("\nERROR: %s\n", err)
}
func GetHash(data string) string {
var ret string
for range Only.Once {
h := md5.New()
h.Write([]byte(data))
ret = hex.EncodeToString(h.Sum(nil))
}
return ret
}
func WriteTempFile(path string, data string) (string, error) {
var fn string
var err error
for range Only.Once {
fn = fmt.Sprintf("%s/%s", os.TempDir(), path)
fmt.Printf("Writing file %s ...", fn)
var fh *os.File
fh, err = os.OpenFile(fn, os.O_RDWR|os.O_CREATE, 0664)
if err != nil {
fmt.Printf("%s\n", err)
break
}
defer fh.Close()
_, err = fh.Write([]byte(data))
if err != nil {
fmt.Printf("%s\n", err)
break
}
fmt.Println("OK")
//fmt.Printf("%v\n", data)
}
return fn, err
}