Merge branch 'tbnobody:master' into master
This commit is contained in:
commit
2719238db0
13
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
13
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -47,7 +47,8 @@ body:
|
|||||||
label: Install Method
|
label: Install Method
|
||||||
description: How did you install OpenDTU?
|
description: How did you install OpenDTU?
|
||||||
options:
|
options:
|
||||||
- Pre-Compiled binary from GitHub
|
- Pre-Compiled binary from GitHub releases
|
||||||
|
- Pre-Compiles binary from GitHub actions/pull-request
|
||||||
- Self-Compiled
|
- Self-Compiled
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
@ -59,6 +60,14 @@ body:
|
|||||||
placeholder: "e.g. 359d513"
|
placeholder: "e.g. 359d513"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: environment
|
||||||
|
attributes:
|
||||||
|
label: What firmware variant (PIO Environment) are you using?
|
||||||
|
description: You can find this in by going to Info -> System
|
||||||
|
placeholder: "generic_esp32s3_usb"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: logs
|
id: logs
|
||||||
attributes:
|
attributes:
|
||||||
@ -84,5 +93,5 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: I have updated the title field above with a concise description.
|
- label: I have updated the title field above with a concise description.
|
||||||
required: true
|
required: true
|
||||||
- label: I have double checked that my inverter does not contain a W in the model name (like HMS-xxxW) as they are not supported
|
- label: I have double checked that my inverter does not contain a W in the model name (like HMS-xxxW) as they are not supported.
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
17
.github/workflows/build.yml
vendored
17
.github/workflows/build.yml
vendored
@ -43,7 +43,7 @@ jobs:
|
|||||||
environments: ${{ steps.envs.outputs.environments }}
|
environments: ${{ steps.envs.outputs.environments }}
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build Enviornments
|
name: Build Environments
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: get_default_envs
|
needs: get_default_envs
|
||||||
strategy:
|
strategy:
|
||||||
@ -79,18 +79,27 @@ jobs:
|
|||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install --upgrade platformio setuptools
|
pip install --upgrade platformio setuptools
|
||||||
|
|
||||||
|
- name: Enable Corepack
|
||||||
|
run: |
|
||||||
|
cd webapp
|
||||||
|
corepack enable
|
||||||
|
|
||||||
- name: Setup Node.js and yarn
|
- name: Setup Node.js and yarn
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: "20"
|
node-version: "22"
|
||||||
cache: "yarn"
|
cache: "yarn"
|
||||||
cache-dependency-path: "webapp/yarn.lock"
|
cache-dependency-path: "webapp/yarn.lock"
|
||||||
|
|
||||||
- name: Install WebApp dependencies
|
- name: Install WebApp dependencies
|
||||||
run: yarn --cwd webapp install --frozen-lockfile
|
run: |
|
||||||
|
cd webapp
|
||||||
|
yarn install --frozen-lockfile
|
||||||
|
|
||||||
- name: Build WebApp
|
- name: Build WebApp
|
||||||
run: yarn --cwd webapp build
|
run: |
|
||||||
|
cd webapp
|
||||||
|
yarn build
|
||||||
|
|
||||||
- name: Build firmware
|
- name: Build firmware
|
||||||
run: pio run -e ${{ matrix.environment }}
|
run: pio run -e ${{ matrix.environment }}
|
||||||
|
|||||||
@ -18,6 +18,12 @@
|
|||||||
"fix"
|
"fix"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "## 🌎 Web Application",
|
||||||
|
"labels": [
|
||||||
|
"webapp"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "## 📚 Documentation",
|
"title": "## 📚 Documentation",
|
||||||
"labels": [
|
"labels": [
|
||||||
|
|||||||
2
.github/workflows/cpplint.yml
vendored
2
.github/workflows/cpplint.yml
vendored
@ -7,7 +7,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
|
|||||||
16
.github/workflows/yarnlint.yml
vendored
16
.github/workflows/yarnlint.yml
vendored
@ -6,17 +6,23 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: webapp
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
- name: Enable Corepack
|
||||||
|
run: corepack enable
|
||||||
- name: Setup Node.js and yarn
|
- name: Setup Node.js and yarn
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: "18"
|
node-version: "22"
|
||||||
cache: "yarn"
|
cache: "yarn"
|
||||||
cache-dependency-path: "webapp/yarn.lock"
|
cache-dependency-path: "webapp/yarn.lock"
|
||||||
|
|
||||||
- name: Install WebApp dependencies
|
- name: Install WebApp dependencies
|
||||||
run: yarn --cwd webapp install --frozen-lockfile
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
- name: Linting
|
- name: Linting
|
||||||
run: yarn --cwd webapp lint
|
run: yarn lint
|
||||||
|
|||||||
28
.github/workflows/yarnprettier.yml
vendored
Normal file
28
.github/workflows/yarnprettier.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
name: Yarn Prettier
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: webapp
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Enable Corepack
|
||||||
|
run: corepack enable
|
||||||
|
- name: Setup Node.js and yarn
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
cache: "yarn"
|
||||||
|
cache-dependency-path: "webapp/yarn.lock"
|
||||||
|
|
||||||
|
- name: Install WebApp dependencies
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Check Formatting
|
||||||
|
run: yarn prettier --check src/
|
||||||
39
README.md
39
README.md
@ -43,41 +43,4 @@ Generated using: `git log --date=short --pretty=format:"* %h%x09%ad%x09%s" | gre
|
|||||||
|
|
||||||
## Currently supported Inverters
|
## Currently supported Inverters
|
||||||
|
|
||||||
| Model | Required RF Module | DC Inputs | MPP-Tracker | AC Phases |
|
A list of all currently supported inverters can be found [here](https://www.opendtu.solar/hardware/inverter_overview/)
|
||||||
| ---------------------| ------------------ | --------- | ----------- | --------- |
|
|
||||||
| Hoymiles HM-300-1T | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HM-350-1T | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HM-400-1T | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HM-600-2T | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HM-700-2T | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HM-800-2T | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HM-1000-4T | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
| Hoymiles HM-1200-4T | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
| Hoymiles HM-1500-4T | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-300-1T | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-350-1T | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-400-1T | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-450-1T | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-500-1T | CMT2300A | 1 | 1 | 1 |
|
|
||||||
| Hoymiles HMS-600-2T | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-700-2T | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-800-2T | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-900-2T | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-1000-2T | CMT2300A | 2 | 2 | 1 |
|
|
||||||
| Hoymiles HMS-1600-4T | CMT2300A | 4 | 4 | 1 |
|
|
||||||
| Hoymiles HMS-1800-4T | CMT2300A | 4 | 4 | 1 |
|
|
||||||
| Hoymiles HMS-2000-4T | CMT2300A | 4 | 4 | 1 |
|
|
||||||
| Hoymiles HMT-1600-4T | CMT2300A | 4 | 2 | 3 |
|
|
||||||
| Hoymiles HMT-1800-4T | CMT2300A | 4 | 2 | 3 |
|
|
||||||
| Hoymiles HMT-2000-4T | CMT2300A | 4 | 2 | 3 |
|
|
||||||
| Hoymiles HMT-1800-6T | CMT2300A | 6 | 3 | 3 |
|
|
||||||
| Hoymiles HMT-2250-6T | CMT2300A | 6 | 3 | 3 |
|
|
||||||
| Solenso SOL-H350 | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Solenso SOL-H400 | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| Solenso SOL-H800 | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| TSUN TSOL-M350 | NRF24L01+ | 1 | 1 | 1 |
|
|
||||||
| TSUN TSOL-M800 | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| TSUN TSOL-M1600 | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
| E-Star HERF-800 | NRF24L01+ | 2 | 2 | 1 |
|
|
||||||
| E-Star HERF-1600 | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
| E-Star HERF-1800 | NRF24L01+ | 4 | 2 | 1 |
|
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v1",
|
"name": "OpenDTU Fusion v1",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -25,6 +28,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v1 with SSD1306 Display",
|
"name": "OpenDTU Fusion v1 with SSD1306 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -54,6 +60,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v1 with SH1106 Display",
|
"name": "OpenDTU Fusion v1 with SH1106 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -83,6 +92,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v2 with CMT2300A and NRF24",
|
"name": "OpenDTU Fusion v2 with CMT2300A and NRF24",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -115,6 +127,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v2 with CMT2300A, NRF24 and SH1106 Display",
|
"name": "OpenDTU Fusion v2 with CMT2300A, NRF24 and SH1106 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -152,6 +167,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "OpenDTU Fusion v2 with CMT2300A, NRF24 and SSD1306 Display",
|
"name": "OpenDTU Fusion v2 with CMT2300A, NRF24 and SSD1306 Display",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
"nrf24": {
|
"nrf24": {
|
||||||
"miso": 48,
|
"miso": 48,
|
||||||
"mosi": 35,
|
"mosi": 35,
|
||||||
@ -186,5 +204,44 @@
|
|||||||
"data": 2,
|
"data": 2,
|
||||||
"clk": 1
|
"clk": 1
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OpenDTU Fusion v2 PoE",
|
||||||
|
"links": [
|
||||||
|
{"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"}
|
||||||
|
],
|
||||||
|
"nrf24": {
|
||||||
|
"miso": 48,
|
||||||
|
"mosi": 35,
|
||||||
|
"clk": 36,
|
||||||
|
"irq": 47,
|
||||||
|
"en": 38,
|
||||||
|
"cs": 37
|
||||||
|
},
|
||||||
|
"cmt": {
|
||||||
|
"clk": 6,
|
||||||
|
"cs": 4,
|
||||||
|
"fcs": 21,
|
||||||
|
"sdio": 5,
|
||||||
|
"gpio2": 3,
|
||||||
|
"gpio3": 8
|
||||||
|
},
|
||||||
|
"w5500": {
|
||||||
|
"mosi": 40,
|
||||||
|
"miso": 41,
|
||||||
|
"sclk": 39,
|
||||||
|
"cs": 42,
|
||||||
|
"int": 44,
|
||||||
|
"rst": 43
|
||||||
|
},
|
||||||
|
"led": {
|
||||||
|
"led0": 17,
|
||||||
|
"led1": 18
|
||||||
|
},
|
||||||
|
"display": {
|
||||||
|
"type": 0,
|
||||||
|
"data": 2,
|
||||||
|
"clk": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -6,7 +6,7 @@
|
|||||||
#include <TaskSchedulerDeclarations.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
// mqtt discovery device classes
|
// mqtt discovery device classes
|
||||||
enum {
|
enum DeviceClassType {
|
||||||
DEVICE_CLS_NONE = 0,
|
DEVICE_CLS_NONE = 0,
|
||||||
DEVICE_CLS_CURRENT,
|
DEVICE_CLS_CURRENT,
|
||||||
DEVICE_CLS_ENERGY,
|
DEVICE_CLS_ENERGY,
|
||||||
@ -15,20 +15,34 @@ enum {
|
|||||||
DEVICE_CLS_FREQ,
|
DEVICE_CLS_FREQ,
|
||||||
DEVICE_CLS_TEMP,
|
DEVICE_CLS_TEMP,
|
||||||
DEVICE_CLS_POWER_FACTOR,
|
DEVICE_CLS_POWER_FACTOR,
|
||||||
DEVICE_CLS_REACTIVE_POWER
|
DEVICE_CLS_REACTIVE_POWER,
|
||||||
|
DEVICE_CLS_CONNECTIVITY,
|
||||||
|
DEVICE_CLS_DURATION,
|
||||||
|
DEVICE_CLS_SIGNAL_STRENGTH,
|
||||||
|
DEVICE_CLS_TEMPERATURE,
|
||||||
|
DEVICE_CLS_RESTART
|
||||||
};
|
};
|
||||||
const char* const deviceClasses[] = { 0, "current", "energy", "power", "voltage", "frequency", "temperature", "power_factor", "reactive_power" };
|
const char* const deviceClass_name[] = { 0, "current", "energy", "power", "voltage", "frequency", "temperature", "power_factor", "reactive_power", "connectivity", "duration", "signal_strength", "temperature", "restart" };
|
||||||
enum {
|
|
||||||
|
enum StateClassType {
|
||||||
STATE_CLS_NONE = 0,
|
STATE_CLS_NONE = 0,
|
||||||
STATE_CLS_MEASUREMENT,
|
STATE_CLS_MEASUREMENT,
|
||||||
STATE_CLS_TOTAL_INCREASING
|
STATE_CLS_TOTAL_INCREASING
|
||||||
};
|
};
|
||||||
const char* const stateClasses[] = { 0, "measurement", "total_increasing" };
|
const char* const stateClass_name[] = { 0, "measurement", "total_increasing" };
|
||||||
|
|
||||||
|
enum CategoryType {
|
||||||
|
CATEGORY_NONE = 0,
|
||||||
|
CATEGORY_CONFIG,
|
||||||
|
CATEGORY_DIAGNOSTIC
|
||||||
|
};
|
||||||
|
const char* const category_name[] = { 0, "config", "diagnostic" };
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FieldId_t fieldId; // field id
|
FieldId_t fieldId; // field id
|
||||||
uint8_t deviceClsId; // device class
|
DeviceClassType deviceClsId; // device class
|
||||||
uint8_t stateClsId; // state class
|
StateClassType stateClsId; // state class
|
||||||
} byteAssign_fieldDeviceClass_t;
|
} byteAssign_fieldDeviceClass_t;
|
||||||
|
|
||||||
const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
|
const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = {
|
||||||
@ -58,13 +72,24 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void loop();
|
void loop();
|
||||||
void publish(const String& subtopic, const String& payload);
|
static void publish(const String& subtopic, const String& payload);
|
||||||
void publishDtuSensor(const char* name, const char* device_class, const char* category, const char* icon, const char* unit_of_measure, const char* subTopic);
|
static void publish(const String& subtopic, const JsonDocument& doc);
|
||||||
void publishDtuBinarySensor(const char* name, const char* device_class, const char* category, const char* payload_on, const char* payload_off, const char* subTopic = "");
|
|
||||||
void publishInverterField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear = false);
|
static void addCommonMetadata(JsonDocument& doc, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category);
|
||||||
void publishInverterButton(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* deviceClass, const char* subTopic, const char* payload);
|
|
||||||
void publishInverterNumber(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* commandTopic, const char* stateTopic, const char* unitOfMeasure, const int16_t min = 1, const int16_t max = 100, float step = 1.0);
|
// Binary Sensor
|
||||||
void publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* subTopic, const char* payload_on, const char* payload_off);
|
static void publishBinarySensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category);
|
||||||
|
static void publishDtuBinarySensor(const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category);
|
||||||
|
static void publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category);
|
||||||
|
|
||||||
|
// Sensor
|
||||||
|
static void publishSensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category);
|
||||||
|
static void publishDtuSensor(const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category);
|
||||||
|
static void publishInverterSensor(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category);
|
||||||
|
|
||||||
|
static void publishInverterField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear = false);
|
||||||
|
static void publishInverterButton(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& payload, const String& icon, const DeviceClassType device_class, const CategoryType category);
|
||||||
|
static void publishInverterNumber(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& command_topic, const int16_t min, const int16_t max, float step, const String& unit_of_measure, const String& icon, const CategoryType category);
|
||||||
|
|
||||||
static void createInverterInfo(JsonDocument& doc, std::shared_ptr<InverterAbstract> inv);
|
static void createInverterInfo(JsonDocument& doc, std::shared_ptr<InverterAbstract> inv);
|
||||||
static void createDtuInfo(JsonDocument& doc);
|
static void createDtuInfo(JsonDocument& doc);
|
||||||
|
|||||||
@ -5,6 +5,8 @@
|
|||||||
#include <Hoymiles.h>
|
#include <Hoymiles.h>
|
||||||
#include <TaskSchedulerDeclarations.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <espMqttClient.h>
|
#include <espMqttClient.h>
|
||||||
|
#include <frozen/map.h>
|
||||||
|
#include <frozen/string.h>
|
||||||
|
|
||||||
class MqttHandleInverterClass {
|
class MqttHandleInverterClass {
|
||||||
public:
|
public:
|
||||||
@ -19,7 +21,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
void loop();
|
void loop();
|
||||||
void publishField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId);
|
void publishField(std::shared_ptr<InverterAbstract> inv, const ChannelType_t type, const ChannelNum_t channel, const FieldId_t fieldId);
|
||||||
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, const size_t len, const size_t index, const size_t total);
|
|
||||||
|
|
||||||
Task _loopTask;
|
Task _loopTask;
|
||||||
|
|
||||||
@ -41,6 +42,29 @@ private:
|
|||||||
FLD_IRR,
|
FLD_IRR,
|
||||||
FLD_Q
|
FLD_Q
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Topic : unsigned {
|
||||||
|
LimitPersistentRelative,
|
||||||
|
LimitPersistentAbsolute,
|
||||||
|
LimitNonPersistentRelative,
|
||||||
|
LimitNonPersistentAbsolute,
|
||||||
|
Power,
|
||||||
|
Restart,
|
||||||
|
ResetRfStats,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr frozen::string _cmdtopic = "+/cmd/";
|
||||||
|
static constexpr frozen::map<frozen::string, Topic, 7> _subscriptions = {
|
||||||
|
{ "limit_persistent_relative", Topic::LimitPersistentRelative },
|
||||||
|
{ "limit_persistent_absolute", Topic::LimitPersistentAbsolute },
|
||||||
|
{ "limit_nonpersistent_relative", Topic::LimitNonPersistentRelative },
|
||||||
|
{ "limit_nonpersistent_absolute", Topic::LimitNonPersistentAbsolute },
|
||||||
|
{ "power", Topic::Power },
|
||||||
|
{ "restart", Topic::Restart },
|
||||||
|
{ "reset_rf_stats", Topic::ResetRfStats },
|
||||||
|
};
|
||||||
|
|
||||||
|
void onMqttMessage(Topic t, const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, const size_t len, const size_t index, const size_t total);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MqttHandleInverterClass MqttHandleInverter;
|
extern MqttHandleInverterClass MqttHandleInverter;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "W5500.h"
|
||||||
#include <DNSServer.h>
|
#include <DNSServer.h>
|
||||||
#include <TaskSchedulerDeclarations.h>
|
#include <TaskSchedulerDeclarations.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
@ -23,10 +24,10 @@ enum class network_event {
|
|||||||
NETWORK_EVENT_MAX
|
NETWORK_EVENT_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::function<void(network_event event)> NetworkEventCb;
|
typedef std::function<void(network_event event)> DtuNetworkEventCb;
|
||||||
|
|
||||||
typedef struct NetworkEventCbList {
|
typedef struct NetworkEventCbList {
|
||||||
NetworkEventCb cb;
|
DtuNetworkEventCb cb;
|
||||||
network_event event;
|
network_event event;
|
||||||
|
|
||||||
NetworkEventCbList()
|
NetworkEventCbList()
|
||||||
@ -53,7 +54,7 @@ public:
|
|||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
network_mode NetworkMode() const;
|
network_mode NetworkMode() const;
|
||||||
|
|
||||||
bool onEvent(NetworkEventCb cbEvent, const network_event event = network_event::NETWORK_EVENT_MAX);
|
bool onEvent(DtuNetworkEventCb cbEvent, const network_event event = network_event::NETWORK_EVENT_MAX);
|
||||||
void raiseEvent(const network_event event);
|
void raiseEvent(const network_event event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -62,7 +63,7 @@ private:
|
|||||||
void setStaticIp();
|
void setStaticIp();
|
||||||
void handleMDNS();
|
void handleMDNS();
|
||||||
void setupMode();
|
void setupMode();
|
||||||
void NetworkEvent(const WiFiEvent_t event);
|
void NetworkEvent(const WiFiEvent_t event, WiFiEventInfo_t info);
|
||||||
|
|
||||||
Task _loopTask;
|
Task _loopTask;
|
||||||
|
|
||||||
@ -83,6 +84,7 @@ private:
|
|||||||
bool _ethConnected = false;
|
bool _ethConnected = false;
|
||||||
std::vector<NetworkEventCbList_t> _cbEventList;
|
std::vector<NetworkEventCbList_t> _cbEventList;
|
||||||
bool _lastMdnsEnabled = false;
|
bool _lastMdnsEnabled = false;
|
||||||
|
std::unique_ptr<W5500> _w5500;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern NetworkSettingsClass NetworkSettings;
|
extern NetworkSettingsClass NetworkSettings;
|
||||||
@ -26,6 +26,13 @@ struct PinMapping_t {
|
|||||||
int8_t cmt_gpio3;
|
int8_t cmt_gpio3;
|
||||||
int8_t cmt_sdio;
|
int8_t cmt_sdio;
|
||||||
|
|
||||||
|
int8_t w5500_mosi;
|
||||||
|
int8_t w5500_miso;
|
||||||
|
int8_t w5500_sclk;
|
||||||
|
int8_t w5500_cs;
|
||||||
|
int8_t w5500_int;
|
||||||
|
int8_t w5500_rst;
|
||||||
|
|
||||||
int8_t eth_phy_addr;
|
int8_t eth_phy_addr;
|
||||||
bool eth_enabled;
|
bool eth_enabled;
|
||||||
int eth_power;
|
int eth_power;
|
||||||
@ -49,6 +56,7 @@ public:
|
|||||||
|
|
||||||
bool isValidNrf24Config() const;
|
bool isValidNrf24Config() const;
|
||||||
bool isValidCmt2300Config() const;
|
bool isValidCmt2300Config() const;
|
||||||
|
bool isValidW5500Config() const;
|
||||||
bool isValidEthConfig() const;
|
bool isValidEthConfig() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
18
include/RestartHelper.h
Normal file
18
include/RestartHelper.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <TaskSchedulerDeclarations.h>
|
||||||
|
|
||||||
|
class RestartHelperClass {
|
||||||
|
public:
|
||||||
|
RestartHelperClass();
|
||||||
|
void init(Scheduler& scheduler);
|
||||||
|
void triggerRestart();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void loop();
|
||||||
|
|
||||||
|
Task _rebootTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern RestartHelperClass RestartHelper;
|
||||||
@ -9,7 +9,6 @@ public:
|
|||||||
static uint32_t getChipId();
|
static uint32_t getChipId();
|
||||||
static uint64_t generateDtuSerial();
|
static uint64_t generateDtuSerial();
|
||||||
static int getTimezoneOffset();
|
static int getTimezoneOffset();
|
||||||
static void restartDtu();
|
|
||||||
static bool checkJsonAlloc(const JsonDocument& doc, const char* function, const uint16_t line);
|
static bool checkJsonAlloc(const JsonDocument& doc, const char* function, const uint16_t line);
|
||||||
static void removeAllFiles();
|
static void removeAllFiles();
|
||||||
};
|
};
|
||||||
|
|||||||
20
include/W5500.h
Normal file
20
include/W5500.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <esp_eth.h> // required for esp_eth_handle_t
|
||||||
|
#include <esp_netif.h>
|
||||||
|
|
||||||
|
class W5500 {
|
||||||
|
public:
|
||||||
|
explicit W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst);
|
||||||
|
W5500(const W5500&) = delete;
|
||||||
|
W5500& operator=(const W5500&) = delete;
|
||||||
|
~W5500();
|
||||||
|
|
||||||
|
String macAddress();
|
||||||
|
|
||||||
|
private:
|
||||||
|
esp_eth_handle_t eth_handle;
|
||||||
|
esp_netif_t* eth_netif;
|
||||||
|
};
|
||||||
@ -32,6 +32,7 @@ enum WebApiError {
|
|||||||
InverterChanged,
|
InverterChanged,
|
||||||
InverterDeleted,
|
InverterDeleted,
|
||||||
InverterOrdered,
|
InverterOrdered,
|
||||||
|
InverterStatsResetted,
|
||||||
|
|
||||||
LimitBase = 5000,
|
LimitBase = 5000,
|
||||||
LimitSerialZero,
|
LimitSerialZero,
|
||||||
|
|||||||
@ -14,4 +14,5 @@ private:
|
|||||||
void onInverterEdit(AsyncWebServerRequest* request);
|
void onInverterEdit(AsyncWebServerRequest* request);
|
||||||
void onInverterDelete(AsyncWebServerRequest* request);
|
void onInverterDelete(AsyncWebServerRequest* request);
|
||||||
void onInverterOrder(AsyncWebServerRequest* request);
|
void onInverterOrder(AsyncWebServerRequest* request);
|
||||||
|
void onInverterStatReset(AsyncWebServerRequest* request);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,4 +5,5 @@
|
|||||||
|
|
||||||
|
|
||||||
extern const char *__COMPILED_GIT_HASH__;
|
extern const char *__COMPILED_GIT_HASH__;
|
||||||
|
extern const char *__COMPILED_GIT_BRANCH__;
|
||||||
// extern const char *__COMPILED_DATE_TIME_UTC_STR__;
|
// extern const char *__COMPILED_DATE_TIME_UTC_STR__;
|
||||||
|
|||||||
@ -1,142 +0,0 @@
|
|||||||
#include "cmt_spi3.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <driver/spi_master.h>
|
|
||||||
#include <esp_rom_gpio.h> // for esp_rom_gpio_connect_out_signal
|
|
||||||
|
|
||||||
SemaphoreHandle_t paramLock = NULL;
|
|
||||||
#define SPI_PARAM_LOCK() \
|
|
||||||
do { \
|
|
||||||
} while (xSemaphoreTake(paramLock, portMAX_DELAY) != pdPASS)
|
|
||||||
#define SPI_PARAM_UNLOCK() xSemaphoreGive(paramLock)
|
|
||||||
|
|
||||||
// for ESP32 this is the so-called HSPI
|
|
||||||
// for ESP32-S2/S3/C3 this nomenclature does not really exist anymore,
|
|
||||||
// it is simply the first externally usable hardware SPI master controller
|
|
||||||
#define SPI_CMT SPI2_HOST
|
|
||||||
|
|
||||||
spi_device_handle_t spi_reg, spi_fifo;
|
|
||||||
|
|
||||||
void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed)
|
|
||||||
{
|
|
||||||
paramLock = xSemaphoreCreateMutex();
|
|
||||||
|
|
||||||
spi_bus_config_t buscfg = {
|
|
||||||
.mosi_io_num = pin_sdio,
|
|
||||||
.miso_io_num = -1, // single wire MOSI/MISO
|
|
||||||
.sclk_io_num = pin_clk,
|
|
||||||
.quadwp_io_num = -1,
|
|
||||||
.quadhd_io_num = -1,
|
|
||||||
.max_transfer_sz = 32,
|
|
||||||
};
|
|
||||||
spi_device_interface_config_t devcfg = {
|
|
||||||
.command_bits = 1,
|
|
||||||
.address_bits = 7,
|
|
||||||
.dummy_bits = 0,
|
|
||||||
.mode = 0, // SPI mode 0
|
|
||||||
.cs_ena_pretrans = 1,
|
|
||||||
.cs_ena_posttrans = 1,
|
|
||||||
.clock_speed_hz = spi_speed,
|
|
||||||
.spics_io_num = pin_cs,
|
|
||||||
.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE,
|
|
||||||
.queue_size = 1,
|
|
||||||
.pre_cb = NULL,
|
|
||||||
.post_cb = NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK(spi_bus_initialize(SPI_CMT, &buscfg, SPI_DMA_DISABLED));
|
|
||||||
ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg, &spi_reg));
|
|
||||||
|
|
||||||
// FiFo
|
|
||||||
spi_device_interface_config_t devcfg2 = {
|
|
||||||
.command_bits = 0,
|
|
||||||
.address_bits = 0,
|
|
||||||
.dummy_bits = 0,
|
|
||||||
.mode = 0, // SPI mode 0
|
|
||||||
.cs_ena_pretrans = 2,
|
|
||||||
.cs_ena_posttrans = (uint8_t)(1 / (spi_speed * 10e6 * 2) + 2), // >2 us
|
|
||||||
.clock_speed_hz = spi_speed,
|
|
||||||
.spics_io_num = pin_fcs,
|
|
||||||
.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE,
|
|
||||||
.queue_size = 1,
|
|
||||||
.pre_cb = NULL,
|
|
||||||
.post_cb = NULL,
|
|
||||||
};
|
|
||||||
ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg2, &spi_fifo));
|
|
||||||
|
|
||||||
esp_rom_gpio_connect_out_signal(pin_sdio, spi_periph_signal[SPI_CMT].spid_out, true, false);
|
|
||||||
delay(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cmt_spi3_write(const uint8_t addr, const uint8_t dat)
|
|
||||||
{
|
|
||||||
uint8_t tx_data;
|
|
||||||
tx_data = ~dat;
|
|
||||||
spi_transaction_t t = {
|
|
||||||
.cmd = 1,
|
|
||||||
.addr = ~addr,
|
|
||||||
.length = 8,
|
|
||||||
.tx_buffer = &tx_data,
|
|
||||||
.rx_buffer = NULL
|
|
||||||
};
|
|
||||||
SPI_PARAM_LOCK();
|
|
||||||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t));
|
|
||||||
SPI_PARAM_UNLOCK();
|
|
||||||
delayMicroseconds(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t cmt_spi3_read(const uint8_t addr)
|
|
||||||
{
|
|
||||||
uint8_t rx_data;
|
|
||||||
spi_transaction_t t = {
|
|
||||||
.cmd = 0,
|
|
||||||
.addr = ~addr,
|
|
||||||
.length = 8,
|
|
||||||
.rxlength = 8,
|
|
||||||
.tx_buffer = NULL,
|
|
||||||
.rx_buffer = &rx_data
|
|
||||||
};
|
|
||||||
SPI_PARAM_LOCK();
|
|
||||||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t));
|
|
||||||
SPI_PARAM_UNLOCK();
|
|
||||||
delayMicroseconds(100);
|
|
||||||
return rx_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len)
|
|
||||||
{
|
|
||||||
uint8_t tx_data;
|
|
||||||
|
|
||||||
spi_transaction_t t = {
|
|
||||||
.length = 8,
|
|
||||||
.tx_buffer = &tx_data, // reference to write data
|
|
||||||
.rx_buffer = NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
SPI_PARAM_LOCK();
|
|
||||||
for (uint8_t i = 0; i < len; i++) {
|
|
||||||
tx_data = ~buf[i]; // negate buffer contents
|
|
||||||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t));
|
|
||||||
delayMicroseconds(4); // > 4 us
|
|
||||||
}
|
|
||||||
SPI_PARAM_UNLOCK();
|
|
||||||
}
|
|
||||||
|
|
||||||
void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len)
|
|
||||||
{
|
|
||||||
uint8_t rx_data;
|
|
||||||
|
|
||||||
spi_transaction_t t = {
|
|
||||||
.length = 8,
|
|
||||||
.rxlength = 8,
|
|
||||||
.tx_buffer = NULL,
|
|
||||||
.rx_buffer = &rx_data
|
|
||||||
};
|
|
||||||
|
|
||||||
SPI_PARAM_LOCK();
|
|
||||||
for (uint8_t i = 0; i < len; i++) {
|
|
||||||
ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t));
|
|
||||||
delayMicroseconds(4); // > 4 us
|
|
||||||
buf[i] = rx_data;
|
|
||||||
}
|
|
||||||
SPI_PARAM_UNLOCK();
|
|
||||||
}
|
|
||||||
155
lib/CMT2300a/cmt_spi3.cpp
Normal file
155
lib/CMT2300a/cmt_spi3.cpp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
#include "cmt_spi3.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <driver/spi_master.h>
|
||||||
|
#include <SpiManager.h>
|
||||||
|
|
||||||
|
SemaphoreHandle_t paramLock = NULL;
|
||||||
|
#define SPI_PARAM_LOCK() \
|
||||||
|
do { \
|
||||||
|
} while (xSemaphoreTake(paramLock, portMAX_DELAY) != pdPASS)
|
||||||
|
#define SPI_PARAM_UNLOCK() xSemaphoreGive(paramLock)
|
||||||
|
|
||||||
|
static void IRAM_ATTR pre_cb(spi_transaction_t *trans) {
|
||||||
|
gpio_set_level(*reinterpret_cast<gpio_num_t*>(trans->user), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void IRAM_ATTR post_cb(spi_transaction_t *trans) {
|
||||||
|
gpio_set_level(*reinterpret_cast<gpio_num_t*>(trans->user), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_device_handle_t spi;
|
||||||
|
gpio_num_t cs_reg, cs_fifo;
|
||||||
|
|
||||||
|
void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int32_t spi_speed)
|
||||||
|
{
|
||||||
|
paramLock = xSemaphoreCreateMutex();
|
||||||
|
|
||||||
|
auto bus_config = std::make_shared<SpiBusConfig>(
|
||||||
|
static_cast<gpio_num_t>(pin_sdio),
|
||||||
|
GPIO_NUM_NC,
|
||||||
|
static_cast<gpio_num_t>(pin_clk)
|
||||||
|
);
|
||||||
|
|
||||||
|
spi_device_interface_config_t device_config {
|
||||||
|
.command_bits = 0, // set by transactions individually
|
||||||
|
.address_bits = 0, // set by transactions individually
|
||||||
|
.dummy_bits = 0,
|
||||||
|
.mode = 0, // SPI mode 0
|
||||||
|
.duty_cycle_pos = 0,
|
||||||
|
.cs_ena_pretrans = 2, // only 1 pre and post cycle would be required for register access
|
||||||
|
.cs_ena_posttrans = static_cast<uint8_t>(2 * spi_speed / 1000000), // >2 us
|
||||||
|
.clock_speed_hz = spi_speed,
|
||||||
|
.input_delay_ns = 0,
|
||||||
|
.spics_io_num = -1, // CS handled by callbacks
|
||||||
|
.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE,
|
||||||
|
.queue_size = 1,
|
||||||
|
.pre_cb = pre_cb,
|
||||||
|
.post_cb = post_cb,
|
||||||
|
};
|
||||||
|
|
||||||
|
spi = SpiManagerInst.alloc_device("", bus_config, device_config);
|
||||||
|
if (!spi)
|
||||||
|
ESP_ERROR_CHECK(ESP_FAIL);
|
||||||
|
|
||||||
|
cs_reg = static_cast<gpio_num_t>(pin_cs);
|
||||||
|
ESP_ERROR_CHECK(gpio_reset_pin(cs_reg));
|
||||||
|
ESP_ERROR_CHECK(gpio_set_level(cs_reg, 1));
|
||||||
|
ESP_ERROR_CHECK(gpio_set_direction(cs_reg, GPIO_MODE_OUTPUT));
|
||||||
|
|
||||||
|
cs_fifo = static_cast<gpio_num_t>(pin_fcs);
|
||||||
|
ESP_ERROR_CHECK(gpio_reset_pin(cs_fifo));
|
||||||
|
ESP_ERROR_CHECK(gpio_set_level(cs_fifo, 1));
|
||||||
|
ESP_ERROR_CHECK(gpio_set_direction(cs_fifo, GPIO_MODE_OUTPUT));
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmt_spi3_write(const uint8_t addr, const uint8_t data)
|
||||||
|
{
|
||||||
|
spi_transaction_ext_t trans {
|
||||||
|
.base {
|
||||||
|
.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR,
|
||||||
|
.cmd = 0,
|
||||||
|
.addr = addr,
|
||||||
|
.length = 8,
|
||||||
|
.rxlength = 0,
|
||||||
|
.user = &cs_reg, // CS for register access
|
||||||
|
.tx_buffer = &data,
|
||||||
|
.rx_buffer = nullptr,
|
||||||
|
},
|
||||||
|
.command_bits = 1,
|
||||||
|
.address_bits = 7,
|
||||||
|
.dummy_bits = 0,
|
||||||
|
};
|
||||||
|
SPI_PARAM_LOCK();
|
||||||
|
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, reinterpret_cast<spi_transaction_t*>(&trans)));
|
||||||
|
SPI_PARAM_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t cmt_spi3_read(const uint8_t addr)
|
||||||
|
{
|
||||||
|
uint8_t data;
|
||||||
|
spi_transaction_ext_t trans {
|
||||||
|
.base {
|
||||||
|
.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR,
|
||||||
|
.cmd = 1,
|
||||||
|
.addr = addr,
|
||||||
|
.length = 0,
|
||||||
|
.rxlength = 8,
|
||||||
|
.user = &cs_reg, // CS for register access
|
||||||
|
.tx_buffer = nullptr,
|
||||||
|
.rx_buffer = &data,
|
||||||
|
},
|
||||||
|
.command_bits = 1,
|
||||||
|
.address_bits = 7,
|
||||||
|
.dummy_bits = 0,
|
||||||
|
};
|
||||||
|
SPI_PARAM_LOCK();
|
||||||
|
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, reinterpret_cast<spi_transaction_t*>(&trans)));
|
||||||
|
SPI_PARAM_UNLOCK();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len)
|
||||||
|
{
|
||||||
|
spi_transaction_t trans {
|
||||||
|
.flags = 0,
|
||||||
|
.cmd = 0,
|
||||||
|
.addr = 0,
|
||||||
|
.length = 8,
|
||||||
|
.rxlength = 0,
|
||||||
|
.user = &cs_fifo, // CS for FIFO access
|
||||||
|
.tx_buffer = nullptr,
|
||||||
|
.rx_buffer = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
SPI_PARAM_LOCK();
|
||||||
|
spi_device_acquire_bus(spi, portMAX_DELAY);
|
||||||
|
for (uint8_t i = 0; i < len; i++) {
|
||||||
|
trans.tx_buffer = buf + i;
|
||||||
|
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &trans));
|
||||||
|
}
|
||||||
|
spi_device_release_bus(spi);
|
||||||
|
SPI_PARAM_UNLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len)
|
||||||
|
{
|
||||||
|
spi_transaction_t trans {
|
||||||
|
.flags = 0,
|
||||||
|
.cmd = 0,
|
||||||
|
.addr = 0,
|
||||||
|
.length = 0,
|
||||||
|
.rxlength = 8,
|
||||||
|
.user = &cs_fifo, // CS for FIFO access
|
||||||
|
.tx_buffer = nullptr,
|
||||||
|
.rx_buffer = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
SPI_PARAM_LOCK();
|
||||||
|
spi_device_acquire_bus(spi, portMAX_DELAY);
|
||||||
|
for (uint8_t i = 0; i < len; i++) {
|
||||||
|
trans.rx_buffer = buf + i;
|
||||||
|
ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &trans));
|
||||||
|
}
|
||||||
|
spi_device_release_bus(spi);
|
||||||
|
SPI_PARAM_UNLOCK();
|
||||||
|
}
|
||||||
@ -3,7 +3,11 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed);
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int32_t spi_speed);
|
||||||
|
|
||||||
void cmt_spi3_write(const uint8_t addr, const uint8_t dat);
|
void cmt_spi3_write(const uint8_t addr, const uint8_t dat);
|
||||||
uint8_t cmt_spi3_read(const uint8_t addr);
|
uint8_t cmt_spi3_read(const uint8_t addr);
|
||||||
@ -11,4 +15,8 @@ uint8_t cmt_spi3_read(const uint8_t addr);
|
|||||||
void cmt_spi3_write_fifo(const uint8_t* p_buf, const uint16_t len);
|
void cmt_spi3_write_fifo(const uint8_t* p_buf, const uint16_t len);
|
||||||
void cmt_spi3_read_fifo(uint8_t* p_buf, const uint16_t len);
|
void cmt_spi3_read_fifo(uint8_t* p_buf, const uint16_t len);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "Hoymiles.h"
|
#include "Hoymiles.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
#include "inverters/HERF_1CH.h"
|
||||||
#include "inverters/HERF_2CH.h"
|
#include "inverters/HERF_2CH.h"
|
||||||
#include "inverters/HERF_4CH.h"
|
#include "inverters/HERF_4CH.h"
|
||||||
#include "inverters/HMS_1CH.h"
|
#include "inverters/HMS_1CH.h"
|
||||||
@ -135,15 +136,7 @@ void HoymilesClass::loop()
|
|||||||
if (currentWeekDay != lastWeekDay) {
|
if (currentWeekDay != lastWeekDay) {
|
||||||
|
|
||||||
for (auto& inv : _inverters) {
|
for (auto& inv : _inverters) {
|
||||||
// Have to reset the offets first, otherwise it will
|
inv->performDailyTask();
|
||||||
// Substract the offset from zero which leads to a high value
|
|
||||||
inv->Statistics()->resetYieldDayCorrection();
|
|
||||||
if (inv->getZeroYieldDayOnMidnight()) {
|
|
||||||
inv->Statistics()->zeroDailyData();
|
|
||||||
}
|
|
||||||
if (inv->getClearEventlogOnMidnight()) {
|
|
||||||
inv->EventLog()->clearBuffer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lastWeekDay = currentWeekDay;
|
lastWeekDay = currentWeekDay;
|
||||||
@ -173,6 +166,8 @@ std::shared_ptr<InverterAbstract> HoymilesClass::addInverter(const char* name, c
|
|||||||
i = std::make_shared<HM_2CH>(_radioNrf.get(), serial);
|
i = std::make_shared<HM_2CH>(_radioNrf.get(), serial);
|
||||||
} else if (HM_1CH::isValidSerial(serial)) {
|
} else if (HM_1CH::isValidSerial(serial)) {
|
||||||
i = std::make_shared<HM_1CH>(_radioNrf.get(), serial);
|
i = std::make_shared<HM_1CH>(_radioNrf.get(), serial);
|
||||||
|
} else if (HERF_1CH::isValidSerial(serial)) {
|
||||||
|
i = std::make_shared<HERF_1CH>(_radioNrf.get(), serial);
|
||||||
} else if (HERF_2CH::isValidSerial(serial)) {
|
} else if (HERF_2CH::isValidSerial(serial)) {
|
||||||
i = std::make_shared<HERF_2CH>(_radioNrf.get(), serial);
|
i = std::make_shared<HERF_2CH>(_radioNrf.get(), serial);
|
||||||
} else if (HERF_4CH::isValidSerial(serial)) {
|
} else if (HERF_4CH::isValidSerial(serial)) {
|
||||||
@ -200,9 +195,9 @@ std::shared_ptr<InverterAbstract> HoymilesClass::getInverterByPos(const uint8_t
|
|||||||
|
|
||||||
std::shared_ptr<InverterAbstract> HoymilesClass::getInverterBySerial(const uint64_t serial)
|
std::shared_ptr<InverterAbstract> HoymilesClass::getInverterBySerial(const uint64_t serial)
|
||||||
{
|
{
|
||||||
for (uint8_t i = 0; i < _inverters.size(); i++) {
|
for (auto& inv : _inverters) {
|
||||||
if (_inverters[i]->serial() == serial) {
|
if (inv->serial() == serial) {
|
||||||
return _inverters[i];
|
return inv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -214,9 +209,7 @@ std::shared_ptr<InverterAbstract> HoymilesClass::getInverterByFragment(const fra
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<InverterAbstract> inv;
|
for (auto& inv : _inverters) {
|
||||||
for (uint8_t i = 0; i < _inverters.size(); i++) {
|
|
||||||
inv = _inverters[i];
|
|
||||||
serial_u p;
|
serial_u p;
|
||||||
p.u64 = inv->serial();
|
p.u64 = inv->serial();
|
||||||
|
|
||||||
|
|||||||
@ -66,16 +66,31 @@ void HoymilesRadio::handleReceivedPackage()
|
|||||||
|
|
||||||
} else if (verifyResult == FRAGMENT_ALL_MISSING_TIMEOUT) {
|
} else if (verifyResult == FRAGMENT_ALL_MISSING_TIMEOUT) {
|
||||||
Hoymiles.getMessageOutput()->println("Nothing received, resend count exeeded");
|
Hoymiles.getMessageOutput()->println("Nothing received, resend count exeeded");
|
||||||
|
// Statistics: Count RX Fail No Answer
|
||||||
|
if (inv->RadioStats.TxRequestData > 0) {
|
||||||
|
inv->RadioStats.RxFailNoAnswer++;
|
||||||
|
}
|
||||||
|
|
||||||
_commandQueue.pop();
|
_commandQueue.pop();
|
||||||
_busyFlag = false;
|
_busyFlag = false;
|
||||||
|
|
||||||
} else if (verifyResult == FRAGMENT_RETRANSMIT_TIMEOUT) {
|
} else if (verifyResult == FRAGMENT_RETRANSMIT_TIMEOUT) {
|
||||||
Hoymiles.getMessageOutput()->println("Retransmit timeout");
|
Hoymiles.getMessageOutput()->println("Retransmit timeout");
|
||||||
|
// Statistics: Count RX Fail Partial Answer
|
||||||
|
if (inv->RadioStats.TxRequestData > 0) {
|
||||||
|
inv->RadioStats.RxFailPartialAnswer++;
|
||||||
|
}
|
||||||
|
|
||||||
_commandQueue.pop();
|
_commandQueue.pop();
|
||||||
_busyFlag = false;
|
_busyFlag = false;
|
||||||
|
|
||||||
} else if (verifyResult == FRAGMENT_HANDLE_ERROR) {
|
} else if (verifyResult == FRAGMENT_HANDLE_ERROR) {
|
||||||
Hoymiles.getMessageOutput()->println("Packet handling error");
|
Hoymiles.getMessageOutput()->println("Packet handling error");
|
||||||
|
// Statistics: Count RX Fail Corrupt Data
|
||||||
|
if (inv->RadioStats.TxRequestData > 0) {
|
||||||
|
inv->RadioStats.RxFailCorruptData++;
|
||||||
|
}
|
||||||
|
|
||||||
_commandQueue.pop();
|
_commandQueue.pop();
|
||||||
_busyFlag = false;
|
_busyFlag = false;
|
||||||
|
|
||||||
@ -83,17 +98,26 @@ void HoymilesRadio::handleReceivedPackage()
|
|||||||
// Perform Retransmit
|
// Perform Retransmit
|
||||||
Hoymiles.getMessageOutput()->print("Request retransmit: ");
|
Hoymiles.getMessageOutput()->print("Request retransmit: ");
|
||||||
Hoymiles.getMessageOutput()->println(verifyResult);
|
Hoymiles.getMessageOutput()->println(verifyResult);
|
||||||
|
// Statistics: Count TX Re-Request Fragment
|
||||||
|
inv->RadioStats.TxReRequestFragment++;
|
||||||
|
|
||||||
sendRetransmitPacket(verifyResult);
|
sendRetransmitPacket(verifyResult);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Successful received all packages
|
// Successful received all packages
|
||||||
Hoymiles.getMessageOutput()->println("Success");
|
Hoymiles.getMessageOutput()->println("Success");
|
||||||
|
// Statistics: Count RX Success
|
||||||
|
if (inv->RadioStats.TxRequestData > 0) {
|
||||||
|
inv->RadioStats.RxSuccess++;
|
||||||
|
}
|
||||||
|
|
||||||
_commandQueue.pop();
|
_commandQueue.pop();
|
||||||
_busyFlag = false;
|
_busyFlag = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If inverter was not found, assume the command is invalid
|
// If inverter was not found, assume the command is invalid
|
||||||
Hoymiles.getMessageOutput()->println("RX: Invalid inverter found");
|
Hoymiles.getMessageOutput()->println("RX: Invalid inverter found");
|
||||||
|
// Statistics: Count RX Fail Unknown Data
|
||||||
_commandQueue.pop();
|
_commandQueue.pop();
|
||||||
_busyFlag = false;
|
_busyFlag = false;
|
||||||
}
|
}
|
||||||
@ -105,6 +129,9 @@ void HoymilesRadio::handleReceivedPackage()
|
|||||||
auto inv = Hoymiles.getInverterBySerial(cmd->getTargetAddress());
|
auto inv = Hoymiles.getInverterBySerial(cmd->getTargetAddress());
|
||||||
if (nullptr != inv) {
|
if (nullptr != inv) {
|
||||||
inv->clearRxFragmentBuffer();
|
inv->clearRxFragmentBuffer();
|
||||||
|
// Statistics: TX Requests
|
||||||
|
inv->RadioStats.TxRequestData++;
|
||||||
|
|
||||||
sendEsbPacket(*cmd);
|
sendEsbPacket(*cmd);
|
||||||
} else {
|
} else {
|
||||||
Hoymiles.getMessageOutput()->println("TX: Invalid inverter found");
|
Hoymiles.getMessageOutput()->println("TX: Invalid inverter found");
|
||||||
|
|||||||
55
lib/Hoymiles/src/inverters/HERF_1CH.cpp
Normal file
55
lib/Hoymiles/src/inverters/HERF_1CH.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022-2024 Thomas Basler and others
|
||||||
|
*/
|
||||||
|
#include "HERF_1CH.h"
|
||||||
|
|
||||||
|
static const byteAssign_t byteAssignment[] = {
|
||||||
|
{ TYPE_DC, CH0, FLD_UDC, UNIT_V, 2, 2, 10, false, 1 },
|
||||||
|
{ TYPE_DC, CH0, FLD_IDC, UNIT_A, 6, 2, 100, false, 2 },
|
||||||
|
{ TYPE_DC, CH0, FLD_PDC, UNIT_W, 10, 2, 10, false, 1 },
|
||||||
|
{ TYPE_DC, CH0, FLD_YD, UNIT_WH, 22, 2, 1, false, 0 },
|
||||||
|
{ TYPE_DC, CH0, FLD_YT, UNIT_KWH, 14, 4, 1000, false, 3 },
|
||||||
|
{ TYPE_DC, CH0, FLD_IRR, UNIT_PCT, CALC_CH_IRR, CH0, CMD_CALC, false, 3 },
|
||||||
|
|
||||||
|
{ TYPE_AC, CH0, FLD_UAC, UNIT_V, 26, 2, 10, false, 1 },
|
||||||
|
{ TYPE_AC, CH0, FLD_IAC, UNIT_A, 34, 2, 100, false, 2 },
|
||||||
|
{ TYPE_AC, CH0, FLD_PAC, UNIT_W, 30, 2, 10, false, 1 },
|
||||||
|
{ TYPE_AC, CH0, FLD_Q, UNIT_VAR, 40, 2, 10, false, 1 }, // to be verified
|
||||||
|
{ TYPE_AC, CH0, FLD_F, UNIT_HZ, 28, 2, 100, false, 2 },
|
||||||
|
{ TYPE_AC, CH0, FLD_PF, UNIT_NONE, 36, 2, 1000, false, 3 },
|
||||||
|
|
||||||
|
{ TYPE_INV, CH0, FLD_T, UNIT_C, 38, 2, 10, true, 1 }, // to be verified
|
||||||
|
{ TYPE_INV, CH0, FLD_EVT_LOG, UNIT_NONE, 40, 2, 1, false, 0 }, // to be verified
|
||||||
|
|
||||||
|
{ TYPE_INV, CH0, FLD_YD, UNIT_WH, CALC_TOTAL_YD, 0, CMD_CALC, false, 0 },
|
||||||
|
{ TYPE_INV, CH0, FLD_YT, UNIT_KWH, CALC_TOTAL_YT, 0, CMD_CALC, false, 3 },
|
||||||
|
{ TYPE_INV, CH0, FLD_PDC, UNIT_W, CALC_TOTAL_PDC, 0, CMD_CALC, false, 1 },
|
||||||
|
{ TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 }
|
||||||
|
};
|
||||||
|
|
||||||
|
HERF_1CH::HERF_1CH(HoymilesRadio* radio, const uint64_t serial)
|
||||||
|
: HM_Abstract(radio, serial) {};
|
||||||
|
|
||||||
|
bool HERF_1CH::isValidSerial(const uint64_t serial)
|
||||||
|
{
|
||||||
|
// serial >= 0x284100000000 && serial <= 0x2841ffffffff
|
||||||
|
uint16_t preSerial = (serial >> 32) & 0xffff;
|
||||||
|
return preSerial == 0x2841;
|
||||||
|
}
|
||||||
|
|
||||||
|
String HERF_1CH::typeName() const
|
||||||
|
{
|
||||||
|
return "HERF-300-1T";
|
||||||
|
}
|
||||||
|
|
||||||
|
const byteAssign_t* HERF_1CH::getByteAssignment() const
|
||||||
|
{
|
||||||
|
return byteAssignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t HERF_1CH::getByteAssignmentSize() const
|
||||||
|
{
|
||||||
|
return sizeof(byteAssignment) / sizeof(byteAssignment[0]);
|
||||||
|
}
|
||||||
13
lib/Hoymiles/src/inverters/HERF_1CH.h
Normal file
13
lib/Hoymiles/src/inverters/HERF_1CH.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "HM_Abstract.h"
|
||||||
|
|
||||||
|
class HERF_1CH : public HM_Abstract {
|
||||||
|
public:
|
||||||
|
explicit HERF_1CH(HoymilesRadio* radio, const uint64_t serial);
|
||||||
|
static bool isValidSerial(const uint64_t serial);
|
||||||
|
String typeName() const;
|
||||||
|
const byteAssign_t* getByteAssignment() const;
|
||||||
|
uint8_t getByteAssignmentSize() const;
|
||||||
|
};
|
||||||
@ -42,7 +42,7 @@ bool HMS_2CH::isValidSerial(const uint64_t serial)
|
|||||||
{
|
{
|
||||||
// serial >= 0x114400000000 && serial <= 0x1144ffffffff
|
// serial >= 0x114400000000 && serial <= 0x1144ffffffff
|
||||||
uint16_t preSerial = (serial >> 32) & 0xffff;
|
uint16_t preSerial = (serial >> 32) & 0xffff;
|
||||||
return preSerial == 0x1144 || preSerial == 0x1143;
|
return preSerial == 0x1144 || preSerial == 0x1143 || preSerial == 0x1410;
|
||||||
}
|
}
|
||||||
|
|
||||||
String HMS_2CH::typeName() const
|
String HMS_2CH::typeName() const
|
||||||
|
|||||||
@ -272,3 +272,22 @@ uint8_t InverterAbstract::verifyAllFragments(CommandAbstract& cmd)
|
|||||||
|
|
||||||
return FRAGMENT_OK;
|
return FRAGMENT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InverterAbstract::performDailyTask()
|
||||||
|
{
|
||||||
|
// Have to reset the offets first, otherwise it will
|
||||||
|
// Substract the offset from zero which leads to a high value
|
||||||
|
Statistics()->resetYieldDayCorrection();
|
||||||
|
if (getZeroYieldDayOnMidnight()) {
|
||||||
|
Statistics()->zeroDailyData();
|
||||||
|
}
|
||||||
|
if (getClearEventlogOnMidnight()) {
|
||||||
|
EventLog()->clearBuffer();
|
||||||
|
}
|
||||||
|
resetRadioStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InverterAbstract::resetRadioStats()
|
||||||
|
{
|
||||||
|
RadioStats = {};
|
||||||
|
}
|
||||||
|
|||||||
@ -65,6 +65,30 @@ public:
|
|||||||
void addRxFragment(const uint8_t fragment[], const uint8_t len);
|
void addRxFragment(const uint8_t fragment[], const uint8_t len);
|
||||||
uint8_t verifyAllFragments(CommandAbstract& cmd);
|
uint8_t verifyAllFragments(CommandAbstract& cmd);
|
||||||
|
|
||||||
|
void performDailyTask();
|
||||||
|
|
||||||
|
void resetRadioStats();
|
||||||
|
|
||||||
|
struct {
|
||||||
|
// TX Request Data
|
||||||
|
uint32_t TxRequestData;
|
||||||
|
|
||||||
|
// TX Re-Request Fragment
|
||||||
|
uint32_t TxReRequestFragment;
|
||||||
|
|
||||||
|
// RX Success
|
||||||
|
uint32_t RxSuccess;
|
||||||
|
|
||||||
|
// RX Fail Partial Answer
|
||||||
|
uint32_t RxFailPartialAnswer;
|
||||||
|
|
||||||
|
// RX Fail No Answer
|
||||||
|
uint32_t RxFailNoAnswer;
|
||||||
|
|
||||||
|
// RX Fail Corrupt Data
|
||||||
|
uint32_t RxFailCorruptData;
|
||||||
|
} RadioStats = {};
|
||||||
|
|
||||||
virtual bool sendStatsRequest() = 0;
|
virtual bool sendStatsRequest() = 0;
|
||||||
virtual bool sendAlarmLogRequest(const bool force = false) = 0;
|
virtual bool sendAlarmLogRequest(const bool force = false) = 0;
|
||||||
virtual bool sendDevInfoRequest() = 0;
|
virtual bool sendDevInfoRequest() = 0;
|
||||||
|
|||||||
@ -1,15 +1,16 @@
|
|||||||
# Class overview
|
# Class overview
|
||||||
|
|
||||||
| Class | Models | Serial range |
|
| Class | Models | Serial range |
|
||||||
| --------------| --------------------------- | ------------ |
|
| --------------| --------------------------- | ------------- -- |
|
||||||
| HM_1CH | HM-300/350/400-1T | 1121 |
|
| HM_1CH | HM-300/350/400-1T | 1121 |
|
||||||
| HM_2CH | HM-600/700/800-2T | 1141 |
|
| HM_2CH | HM-600/700/800-2T | 1141 |
|
||||||
| HM_4CH | HM-1000/1200/1500-4T | 1161 |
|
| HM_4CH | HM-1000/1200/1500-4T | 1161 |
|
||||||
| HMS_1CH | HMS-300/350/400/450/500-1T | 1124 |
|
| HMS_1CH | HMS-300/350/400/450/500-1T | 1124 |
|
||||||
| HMS_1CHv2 | HMS-500-1T v2 | 1125 |
|
| HMS_1CHv2 | HMS-500-1T v2 | 1125 |
|
||||||
| HMS_2CH | HMS-600/700/800/900/1000-2T | 1143, 1144 |
|
| HMS_2CH | HMS-600/700/800/900/1000-2T | 1143, 1144, 1410 |
|
||||||
| HMS_4CH | HMS-1600/1800/2000-4T | 1164 |
|
| HMS_4CH | HMS-1600/1800/2000-4T | 1164 |
|
||||||
| HMT_4CH | HMT-1600/1800/2000-4T | 1361 |
|
| HMT_4CH | HMT-1600/1800/2000-4T | 1361 |
|
||||||
| HMT_6CH | HMT-1800/2250-6T | 1382 |
|
| HMT_6CH | HMT-1800/2250-6T | 1382 |
|
||||||
| HERF_2CH | HERF 800 | 2821 |
|
| HERF_1CH | HERF 300 | 2841 |
|
||||||
| HERF_4CH | HERF 1800 | 2801 |
|
| HERF_2CH | HERF 800 | 2821 |
|
||||||
|
| HERF_4CH | HERF 1800 | 2801 |
|
||||||
|
|||||||
13
lib/SpiManager/library.json
Normal file
13
lib/SpiManager/library.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "SpiManager",
|
||||||
|
"keywords": "spi",
|
||||||
|
"description": "Library for managing the allocation of dedicated or shared SPI buses on the ESP32.",
|
||||||
|
"authors": {
|
||||||
|
"name": "Lennart Ferlemann"
|
||||||
|
},
|
||||||
|
"version": "0.0.1",
|
||||||
|
"frameworks": "arduino",
|
||||||
|
"platforms": [
|
||||||
|
"espressif32"
|
||||||
|
]
|
||||||
|
}
|
||||||
52
lib/SpiManager/src/SpiBus.cpp
Normal file
52
lib/SpiManager/src/SpiBus.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#include "SpiBus.h"
|
||||||
|
#include "SpiBusConfig.h"
|
||||||
|
#include "SpiCallback.h"
|
||||||
|
|
||||||
|
SpiBus::SpiBus(const std::string& _id, spi_host_device_t _host_device)
|
||||||
|
: id(_id)
|
||||||
|
, host_device(_host_device)
|
||||||
|
, cur_config(nullptr)
|
||||||
|
{
|
||||||
|
spi_bus_config_t bus_config {
|
||||||
|
.mosi_io_num = -1,
|
||||||
|
.miso_io_num = -1,
|
||||||
|
.sclk_io_num = -1,
|
||||||
|
.quadwp_io_num = -1,
|
||||||
|
.quadhd_io_num = -1,
|
||||||
|
.data4_io_num = -1,
|
||||||
|
.data5_io_num = -1,
|
||||||
|
.data6_io_num = -1,
|
||||||
|
.data7_io_num = -1,
|
||||||
|
.max_transfer_sz = SPI_MAX_DMA_LEN,
|
||||||
|
.flags = 0,
|
||||||
|
.intr_flags = 0
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(spi_bus_initialize(host_device, &bus_config, SPI_DMA_CH_AUTO));
|
||||||
|
}
|
||||||
|
|
||||||
|
SpiBus::~SpiBus()
|
||||||
|
{
|
||||||
|
ESP_ERROR_CHECK(spi_bus_free(host_device));
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_device_handle_t SpiBus::add_device(const std::shared_ptr<SpiBusConfig>& bus_config, spi_device_interface_config_t& device_config)
|
||||||
|
{
|
||||||
|
if (!SpiCallback::patch(shared_from_this(), bus_config, device_config))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
spi_device_handle_t device;
|
||||||
|
ESP_ERROR_CHECK(spi_bus_add_device(host_device, &device_config, &device));
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add remove_device (with spi_device_acquire_bus)
|
||||||
|
|
||||||
|
void SpiBus::apply_config(SpiBusConfig* config)
|
||||||
|
{
|
||||||
|
if (cur_config)
|
||||||
|
cur_config->unpatch(host_device);
|
||||||
|
cur_config = config;
|
||||||
|
if (cur_config)
|
||||||
|
cur_config->patch(host_device);
|
||||||
|
}
|
||||||
49
lib/SpiManager/src/SpiBus.h
Normal file
49
lib/SpiManager/src/SpiBus.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <driver/spi_master.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class SpiBusConfig;
|
||||||
|
|
||||||
|
class SpiBus : public std::enable_shared_from_this<SpiBus> {
|
||||||
|
public:
|
||||||
|
explicit SpiBus(const std::string& id, spi_host_device_t host_device);
|
||||||
|
SpiBus(const SpiBus&) = delete;
|
||||||
|
SpiBus& operator=(const SpiBus&) = delete;
|
||||||
|
~SpiBus();
|
||||||
|
|
||||||
|
inline __attribute__((always_inline)) void require_config(SpiBusConfig* config)
|
||||||
|
{
|
||||||
|
if (config == cur_config)
|
||||||
|
return;
|
||||||
|
apply_config(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline __attribute__((always_inline)) void free_config(SpiBusConfig* config)
|
||||||
|
{
|
||||||
|
if (config != cur_config)
|
||||||
|
return;
|
||||||
|
apply_config(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const std::string& get_id() const
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline spi_host_device_t get_host_device() const
|
||||||
|
{
|
||||||
|
return host_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_device_handle_t add_device(const std::shared_ptr<SpiBusConfig>& bus_config, spi_device_interface_config_t& device_config);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void apply_config(SpiBusConfig* config);
|
||||||
|
|
||||||
|
std::string id;
|
||||||
|
spi_host_device_t host_device;
|
||||||
|
SpiBusConfig* cur_config;
|
||||||
|
};
|
||||||
71
lib/SpiManager/src/SpiBusConfig.cpp
Normal file
71
lib/SpiManager/src/SpiBusConfig.cpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#include "SpiBusConfig.h"
|
||||||
|
|
||||||
|
#include <driver/gpio.h>
|
||||||
|
#include <esp_rom_gpio.h>
|
||||||
|
#include <soc/spi_periph.h>
|
||||||
|
|
||||||
|
SpiBusConfig::SpiBusConfig(gpio_num_t _pin_mosi, gpio_num_t _pin_miso, gpio_num_t _pin_sclk)
|
||||||
|
: pin_mosi(_pin_mosi)
|
||||||
|
, pin_miso(_pin_miso)
|
||||||
|
, pin_sclk(_pin_sclk)
|
||||||
|
{
|
||||||
|
if (pin_mosi != GPIO_NUM_NC) {
|
||||||
|
ESP_ERROR_CHECK(gpio_reset_pin(pin_mosi));
|
||||||
|
ESP_ERROR_CHECK(gpio_set_direction(pin_mosi, GPIO_MODE_INPUT_OUTPUT));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pin_miso != GPIO_NUM_NC) {
|
||||||
|
ESP_ERROR_CHECK(gpio_reset_pin(pin_miso));
|
||||||
|
ESP_ERROR_CHECK(gpio_set_direction(pin_miso, GPIO_MODE_INPUT));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pin_sclk != GPIO_NUM_NC) {
|
||||||
|
ESP_ERROR_CHECK(gpio_reset_pin(pin_sclk));
|
||||||
|
ESP_ERROR_CHECK(gpio_set_direction(pin_sclk, GPIO_MODE_INPUT_OUTPUT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SpiBusConfig::~SpiBusConfig()
|
||||||
|
{
|
||||||
|
if (pin_mosi != GPIO_NUM_NC)
|
||||||
|
ESP_ERROR_CHECK(gpio_reset_pin(pin_mosi));
|
||||||
|
|
||||||
|
if (pin_miso != GPIO_NUM_NC)
|
||||||
|
ESP_ERROR_CHECK(gpio_reset_pin(pin_miso));
|
||||||
|
|
||||||
|
if (pin_sclk != GPIO_NUM_NC)
|
||||||
|
ESP_ERROR_CHECK(gpio_reset_pin(pin_sclk));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpiBusConfig::patch(spi_host_device_t host_device)
|
||||||
|
{
|
||||||
|
if (pin_mosi != GPIO_NUM_NC) {
|
||||||
|
esp_rom_gpio_connect_out_signal(pin_mosi, spi_periph_signal[host_device].spid_out, false, false);
|
||||||
|
esp_rom_gpio_connect_in_signal(pin_mosi, spi_periph_signal[host_device].spid_in, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pin_miso != GPIO_NUM_NC)
|
||||||
|
esp_rom_gpio_connect_in_signal(pin_miso, spi_periph_signal[host_device].spiq_in, false);
|
||||||
|
|
||||||
|
if (pin_sclk != GPIO_NUM_NC) {
|
||||||
|
esp_rom_gpio_connect_out_signal(pin_sclk, spi_periph_signal[host_device].spiclk_out, false, false);
|
||||||
|
esp_rom_gpio_connect_in_signal(pin_sclk, spi_periph_signal[host_device].spiclk_in, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpiBusConfig::unpatch(spi_host_device_t host_device)
|
||||||
|
{
|
||||||
|
if (pin_mosi != GPIO_NUM_NC) {
|
||||||
|
esp_rom_gpio_connect_out_signal(pin_mosi, SIG_GPIO_OUT_IDX, false, false);
|
||||||
|
esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host_device].spid_in, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pin_miso != GPIO_NUM_NC)
|
||||||
|
esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host_device].spiq_in, false);
|
||||||
|
|
||||||
|
if (pin_sclk != GPIO_NUM_NC) {
|
||||||
|
esp_rom_gpio_connect_out_signal(pin_sclk, SIG_GPIO_OUT_IDX, false, false);
|
||||||
|
esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host_device].spiclk_in, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
lib/SpiManager/src/SpiBusConfig.h
Normal file
21
lib/SpiManager/src/SpiBusConfig.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <hal/gpio_types.h>
|
||||||
|
#include <hal/spi_types.h>
|
||||||
|
|
||||||
|
class SpiBusConfig {
|
||||||
|
public:
|
||||||
|
explicit SpiBusConfig(gpio_num_t pin_mosi, gpio_num_t pin_miso, gpio_num_t pin_sclk);
|
||||||
|
SpiBusConfig(const SpiBusConfig&) = delete;
|
||||||
|
SpiBusConfig& operator=(const SpiBusConfig&) = delete;
|
||||||
|
~SpiBusConfig();
|
||||||
|
|
||||||
|
void patch(spi_host_device_t host_device);
|
||||||
|
void unpatch(spi_host_device_t host_device);
|
||||||
|
|
||||||
|
private:
|
||||||
|
gpio_num_t pin_mosi;
|
||||||
|
gpio_num_t pin_miso;
|
||||||
|
gpio_num_t pin_sclk;
|
||||||
|
};
|
||||||
69
lib/SpiManager/src/SpiCallback.cpp
Normal file
69
lib/SpiManager/src/SpiCallback.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#include "SpiCallback.h"
|
||||||
|
|
||||||
|
#include "SpiBus.h"
|
||||||
|
#include <array>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace SpiCallback {
|
||||||
|
namespace {
|
||||||
|
struct CallbackData {
|
||||||
|
std::shared_ptr<SpiBus> bus;
|
||||||
|
std::shared_ptr<SpiBusConfig> config;
|
||||||
|
transaction_cb_t inner_pre_cb;
|
||||||
|
transaction_cb_t inner_post_cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::array<std::optional<CallbackData>, SPI_MANAGER_CALLBACK_COUNT> instances;
|
||||||
|
|
||||||
|
template <int N>
|
||||||
|
void IRAM_ATTR fn_pre_cb(spi_transaction_t* trans)
|
||||||
|
{
|
||||||
|
instances[N]->bus->require_config(instances[N]->config.get());
|
||||||
|
if (instances[N]->inner_pre_cb)
|
||||||
|
instances[N]->inner_pre_cb(trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int N>
|
||||||
|
void IRAM_ATTR fn_post_cb(spi_transaction_t* trans)
|
||||||
|
{
|
||||||
|
if (instances[N]->inner_post_cb)
|
||||||
|
instances[N]->inner_post_cb(trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int N>
|
||||||
|
inline __attribute__((always_inline)) bool alloc(CallbackData*& instance, transaction_cb_t& pre_cb, transaction_cb_t& post_cb)
|
||||||
|
{
|
||||||
|
if constexpr (N > 0) {
|
||||||
|
if (alloc<N - 1>(instance, pre_cb, post_cb))
|
||||||
|
return true;
|
||||||
|
if (!instances[N - 1]) {
|
||||||
|
instances[N - 1].emplace();
|
||||||
|
instance = &*instances[N - 1];
|
||||||
|
pre_cb = fn_pre_cb<N - 1>;
|
||||||
|
post_cb = fn_post_cb<N - 1>;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool patch(const std::shared_ptr<SpiBus>& bus, const std::shared_ptr<SpiBusConfig>& bus_config, spi_device_interface_config_t& device_config)
|
||||||
|
{
|
||||||
|
CallbackData* instance;
|
||||||
|
transaction_cb_t pre_cb;
|
||||||
|
transaction_cb_t post_cb;
|
||||||
|
if (!alloc<SPI_MANAGER_CALLBACK_COUNT>(instance, pre_cb, post_cb))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
instance->bus = bus;
|
||||||
|
instance->config = bus_config;
|
||||||
|
instance->inner_pre_cb = device_config.pre_cb;
|
||||||
|
instance->inner_post_cb = device_config.post_cb;
|
||||||
|
device_config.pre_cb = pre_cb;
|
||||||
|
device_config.post_cb = post_cb;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
lib/SpiManager/src/SpiCallback.h
Normal file
15
lib/SpiManager/src/SpiCallback.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <driver/spi_master.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// Pre and post callbacks for 2 buses with 3 devices each
|
||||||
|
#define SPI_MANAGER_CALLBACK_COUNT 6
|
||||||
|
|
||||||
|
class SpiBus;
|
||||||
|
class SpiBusConfig;
|
||||||
|
|
||||||
|
namespace SpiCallback {
|
||||||
|
bool patch(const std::shared_ptr<SpiBus>& bus, const std::shared_ptr<SpiBusConfig>& bus_config, spi_device_interface_config_t& device_config);
|
||||||
|
}
|
||||||
114
lib/SpiManager/src/SpiManager.cpp
Normal file
114
lib/SpiManager/src/SpiManager.cpp
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#include "SpiManager.h"
|
||||||
|
|
||||||
|
#ifdef ARDUINO
|
||||||
|
#include <SPI.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SpiManager::SpiManager()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ARDUINO
|
||||||
|
|
||||||
|
std::optional<uint8_t> SpiManager::to_arduino(spi_host_device_t host_device)
|
||||||
|
{
|
||||||
|
switch (host_device) {
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
case SPI1_HOST:
|
||||||
|
return FSPI;
|
||||||
|
case SPI2_HOST:
|
||||||
|
return HSPI;
|
||||||
|
case SPI3_HOST:
|
||||||
|
return VSPI;
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
||||||
|
case SPI2_HOST:
|
||||||
|
return FSPI;
|
||||||
|
case SPI3_HOST:
|
||||||
|
return HSPI;
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||||
|
case SPI2_HOST:
|
||||||
|
return FSPI;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool SpiManager::register_bus(spi_host_device_t host_device)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < SPI_MANAGER_NUM_BUSES; ++i) {
|
||||||
|
if (available_buses[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
available_buses[i] = host_device;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpiManager::claim_bus(spi_host_device_t& host_device)
|
||||||
|
{
|
||||||
|
for (int i = SPI_MANAGER_NUM_BUSES - 1; i >= 0; --i) {
|
||||||
|
if (!available_buses[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
host_device = *available_buses[i];
|
||||||
|
available_buses[i].reset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ARDUINO
|
||||||
|
|
||||||
|
std::optional<uint8_t> SpiManager::claim_bus_arduino()
|
||||||
|
{
|
||||||
|
spi_host_device_t host_device;
|
||||||
|
if (!claim_bus(host_device))
|
||||||
|
return std::nullopt;
|
||||||
|
return to_arduino(host_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
spi_device_handle_t SpiManager::alloc_device(const std::string& bus_id, const std::shared_ptr<SpiBusConfig>& bus_config, spi_device_interface_config_t& device_config)
|
||||||
|
{
|
||||||
|
std::shared_ptr<SpiBus> shared_bus = get_shared_bus(bus_id);
|
||||||
|
if (!shared_bus)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return shared_bus->add_device(bus_config, device_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<SpiBus> SpiManager::get_shared_bus(const std::string& bus_id)
|
||||||
|
{
|
||||||
|
// look for existing shared bus
|
||||||
|
for (int i = 0; i < SPI_MANAGER_NUM_BUSES; ++i) {
|
||||||
|
if (!shared_buses[i])
|
||||||
|
continue;
|
||||||
|
if (shared_buses[i]->get_id() == bus_id)
|
||||||
|
return shared_buses[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new shared bus
|
||||||
|
for (int i = 0; i < SPI_MANAGER_NUM_BUSES; ++i) {
|
||||||
|
if (shared_buses[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
spi_host_device_t host_device;
|
||||||
|
if (!claim_bus(host_device))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
shared_buses[i] = std::make_shared<SpiBus>(bus_id, host_device);
|
||||||
|
return shared_buses[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpiManager SpiManagerInst;
|
||||||
41
lib/SpiManager/src/SpiManager.h
Normal file
41
lib/SpiManager/src/SpiManager.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "SpiBus.h"
|
||||||
|
#include "SpiBusConfig.h"
|
||||||
|
|
||||||
|
#include <driver/spi_master.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#define SPI_MANAGER_NUM_BUSES SOC_SPI_PERIPH_NUM
|
||||||
|
|
||||||
|
class SpiManager {
|
||||||
|
public:
|
||||||
|
explicit SpiManager();
|
||||||
|
SpiManager(const SpiManager&) = delete;
|
||||||
|
SpiManager& operator=(const SpiManager&) = delete;
|
||||||
|
|
||||||
|
#ifdef ARDUINO
|
||||||
|
static std::optional<uint8_t> to_arduino(spi_host_device_t host_device);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool register_bus(spi_host_device_t host_device);
|
||||||
|
bool claim_bus(spi_host_device_t& host_device);
|
||||||
|
#ifdef ARDUINO
|
||||||
|
std::optional<uint8_t> claim_bus_arduino();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
spi_device_handle_t alloc_device(const std::string& bus_id, const std::shared_ptr<SpiBusConfig>& bus_config, spi_device_interface_config_t& device_config);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<SpiBus> get_shared_bus(const std::string& bus_id);
|
||||||
|
|
||||||
|
std::array<std::optional<spi_host_device_t>, SPI_MANAGER_NUM_BUSES> available_buses;
|
||||||
|
std::array<std::shared_ptr<SpiBus>, SPI_MANAGER_NUM_BUSES> shared_buses;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern SpiManager SpiManagerInst;
|
||||||
@ -36,9 +36,20 @@ def get_build_version():
|
|||||||
return build_version
|
return build_version
|
||||||
|
|
||||||
|
|
||||||
|
def get_build_branch():
|
||||||
|
try:
|
||||||
|
branch_name = porcelain.active_branch('.').decode('utf-8') # '.' refers to the repository root dir
|
||||||
|
except Exception as err:
|
||||||
|
branch_name = "master"
|
||||||
|
print("Firmware Branch: " + branch_name)
|
||||||
|
return branch_name
|
||||||
|
|
||||||
|
|
||||||
def get_firmware_specifier_build_flag():
|
def get_firmware_specifier_build_flag():
|
||||||
build_version = get_build_version()
|
build_version = get_build_version()
|
||||||
build_flag = "-D AUTO_GIT_HASH=\\\"" + build_version + "\\\""
|
build_flag = "-D AUTO_GIT_HASH=\\\"" + build_version + "\\\""
|
||||||
|
build_branch = get_build_branch()
|
||||||
|
build_flag += " -D AUTO_GIT_BRANCH=\\\"" + branch_name + "\\\""
|
||||||
return (build_flag)
|
return (build_flag)
|
||||||
|
|
||||||
|
|
||||||
@ -64,6 +75,8 @@ def do_main():
|
|||||||
if 1:
|
if 1:
|
||||||
# Add the description of the current git revision
|
# Add the description of the current git revision
|
||||||
lines += 'const char *__COMPILED_GIT_HASH__ = "%s";\n' % (get_build_version())
|
lines += 'const char *__COMPILED_GIT_HASH__ = "%s";\n' % (get_build_version())
|
||||||
|
# ... and git branch
|
||||||
|
lines += 'const char *__COMPILED_GIT_BRANCH__ = "%s";\n' % (get_build_branch())
|
||||||
|
|
||||||
updateFileIfChanged(targetfile, bytes(lines, "utf-8"))
|
updateFileIfChanged(targetfile, bytes(lines, "utf-8"))
|
||||||
|
|
||||||
|
|||||||
@ -18,20 +18,64 @@
|
|||||||
|
|
||||||
Import("env")
|
Import("env")
|
||||||
|
|
||||||
|
env = DefaultEnvironment()
|
||||||
platform = env.PioPlatform()
|
platform = env.PioPlatform()
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from os.path import join, getsize
|
import csv
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
from os.path import join, getsize, exists, isdir
|
||||||
|
from os import listdir
|
||||||
|
|
||||||
sys.path.append(join(platform.get_package_dir("tool-esptoolpy")))
|
sys.path.append(join(platform.get_package_dir("tool-esptoolpy")))
|
||||||
import esptool
|
import esptool
|
||||||
|
|
||||||
|
def esp32_build_filesystem(fs_name, fs_size):
|
||||||
|
filesystem_dir = env.subst("$PROJECT_DATA_DIR")
|
||||||
|
print("Creating %dKiB filesystem with content:" % (int(fs_size, 0)/1024) )
|
||||||
|
if not isdir(filesystem_dir) or not listdir(filesystem_dir):
|
||||||
|
print("No files added -> will NOT create littlefs.bin and NOT overwrite fs partition!")
|
||||||
|
return False
|
||||||
|
# this does not work on GitHub, results in 'mklittlefs: No such file or directory'
|
||||||
|
tool = shutil.which(env.subst(env["MKFSTOOL"]))
|
||||||
|
if tool is None or not exists(tool):
|
||||||
|
print("Using fallback mklittlefs")
|
||||||
|
tool = "~/.platformio/packages/tool-mklittlefs/mklittlefs"
|
||||||
|
|
||||||
|
cmd = (tool, "-c", filesystem_dir, "-s", fs_size, fs_name)
|
||||||
|
returncode = subprocess.call(cmd, shell=False)
|
||||||
|
print("Return Code:", returncode)
|
||||||
|
return True
|
||||||
|
|
||||||
def esp32_create_combined_bin(source, target, env):
|
def esp32_create_combined_bin(source, target, env):
|
||||||
print("Generating combined binary for serial flashing")
|
print("Generating combined binary for serial flashing")
|
||||||
|
|
||||||
# The offset from begin of the file where the app0 partition starts
|
# The offset from begin of the file where the app0 partition starts
|
||||||
# This is defined in the partition .csv file
|
# This is defined in the partition .csv file
|
||||||
app_offset = 0x10000
|
app_offset = 0x10000
|
||||||
|
fs_offset = -1
|
||||||
|
fs_name = env.subst("$BUILD_DIR/littlefs.bin")
|
||||||
|
|
||||||
|
with open(env.BoardConfig().get("build.partitions")) as csv_file:
|
||||||
|
print("Read partitions from ", env.BoardConfig().get("build.partitions"))
|
||||||
|
csv_reader = csv.reader(csv_file, delimiter=',')
|
||||||
|
line_count = 0
|
||||||
|
for row in csv_reader:
|
||||||
|
if line_count == 0:
|
||||||
|
print(f'{", ".join(row)}')
|
||||||
|
line_count += 1
|
||||||
|
else:
|
||||||
|
if (len(row) < 4):
|
||||||
|
continue
|
||||||
|
print(f'{row[0]} {row[1]} {row[2]} {row[3]} {row[4]}')
|
||||||
|
line_count += 1
|
||||||
|
if(row[0] == 'app0'):
|
||||||
|
app_offset = int(row[3], base=16)
|
||||||
|
elif(row[0] == 'spiffs'):
|
||||||
|
partition_size = row[4]
|
||||||
|
if esp32_build_filesystem(fs_name, partition_size):
|
||||||
|
fs_offset = int(row[3], base=16)
|
||||||
|
|
||||||
new_file_name = env.subst("$BUILD_DIR/${PROGNAME}.factory.bin")
|
new_file_name = env.subst("$BUILD_DIR/${PROGNAME}.factory.bin")
|
||||||
sections = env.subst(env.get("FLASH_EXTRA_IMAGES"))
|
sections = env.subst(env.get("FLASH_EXTRA_IMAGES"))
|
||||||
@ -77,6 +121,10 @@ def esp32_create_combined_bin(source, target, env):
|
|||||||
print(f" - {hex(app_offset)} | {firmware_name}")
|
print(f" - {hex(app_offset)} | {firmware_name}")
|
||||||
cmd += [hex(app_offset), firmware_name]
|
cmd += [hex(app_offset), firmware_name]
|
||||||
|
|
||||||
|
if fs_offset != -1:
|
||||||
|
print(f" - {hex(fs_offset)} | {fs_name}")
|
||||||
|
cmd += [hex(fs_offset), fs_name]
|
||||||
|
|
||||||
print('Using esptool.py arguments: %s' % ' '.join(cmd))
|
print('Using esptool.py arguments: %s' % ' '.join(cmd))
|
||||||
|
|
||||||
esptool.main(cmd)
|
esptool.main(cmd)
|
||||||
|
|||||||
@ -19,7 +19,9 @@ extra_configs =
|
|||||||
custom_ci_action = generic,generic_esp32,generic_esp32s3,generic_esp32s3_usb
|
custom_ci_action = generic,generic_esp32,generic_esp32s3,generic_esp32s3_usb
|
||||||
|
|
||||||
framework = arduino
|
framework = arduino
|
||||||
platform = espressif32@6.8.1
|
platform = espressif32@6.9.0
|
||||||
|
platform_packages =
|
||||||
|
platformio/tool-mklittlefs
|
||||||
|
|
||||||
build_flags =
|
build_flags =
|
||||||
-DPIOENV=\"$PIOENV\"
|
-DPIOENV=\"$PIOENV\"
|
||||||
@ -39,13 +41,13 @@ build_unflags =
|
|||||||
-std=gnu++11
|
-std=gnu++11
|
||||||
|
|
||||||
lib_deps =
|
lib_deps =
|
||||||
mathieucarbou/ESPAsyncWebServer @ 3.1.2
|
mathieucarbou/ESPAsyncWebServer @ 3.3.1
|
||||||
bblanchon/ArduinoJson @ 7.1.0
|
bblanchon/ArduinoJson @ 7.2.0
|
||||||
https://github.com/bertmelis/espMqttClient.git#v1.7.0
|
https://github.com/bertmelis/espMqttClient.git#v1.7.0
|
||||||
nrf24/RF24 @ 1.4.9
|
nrf24/RF24 @ 1.4.9
|
||||||
olikraus/U8g2 @ 2.35.19
|
olikraus/U8g2 @ 2.35.30
|
||||||
buelowp/sunset @ 1.1.7
|
buelowp/sunset @ 1.1.7
|
||||||
https://github.com/arkhipenko/TaskScheduler#testing
|
arkhipenko/TaskScheduler @ 3.8.5
|
||||||
|
|
||||||
extra_scripts =
|
extra_scripts =
|
||||||
pre:pio-scripts/auto_firmware_version.py
|
pre:pio-scripts/auto_firmware_version.py
|
||||||
@ -227,6 +229,7 @@ build_flags = ${env.build_flags}
|
|||||||
-DLED0=17
|
-DLED0=17
|
||||||
-DLED1=18
|
-DLED1=18
|
||||||
-DARDUINO_USB_MODE=1
|
-DARDUINO_USB_MODE=1
|
||||||
|
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||||
|
|
||||||
[env:opendtufusionv2]
|
[env:opendtufusionv2]
|
||||||
board = esp32-s3-devkitc-1
|
board = esp32-s3-devkitc-1
|
||||||
@ -250,3 +253,32 @@ build_flags = ${env.build_flags}
|
|||||||
-DCMT_SDIO=5
|
-DCMT_SDIO=5
|
||||||
-DARDUINO_USB_MODE=1
|
-DARDUINO_USB_MODE=1
|
||||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||||
|
|
||||||
|
[env:opendtufusionv2_shield]
|
||||||
|
board = esp32-s3-devkitc-1
|
||||||
|
upload_protocol = esp-builtin
|
||||||
|
debug_tool = esp-builtin
|
||||||
|
debug_speed = 12000
|
||||||
|
build_flags = ${env.build_flags}
|
||||||
|
-DHOYMILES_PIN_MISO=48
|
||||||
|
-DHOYMILES_PIN_MOSI=35
|
||||||
|
-DHOYMILES_PIN_SCLK=36
|
||||||
|
-DHOYMILES_PIN_IRQ=47
|
||||||
|
-DHOYMILES_PIN_CE=38
|
||||||
|
-DHOYMILES_PIN_CS=37
|
||||||
|
-DLED0=17
|
||||||
|
-DLED1=18
|
||||||
|
-DCMT_CLK=6
|
||||||
|
-DCMT_CS=4
|
||||||
|
-DCMT_FCS=21
|
||||||
|
-DCMT_GPIO2=3
|
||||||
|
-DCMT_GPIO3=8
|
||||||
|
-DCMT_SDIO=5
|
||||||
|
-DW5500_MOSI=40
|
||||||
|
-DW5500_MISO=41
|
||||||
|
-DW5500_SCLK=39
|
||||||
|
-DW5500_CS=42
|
||||||
|
-DW5500_INT=44
|
||||||
|
-DW5500_RST=43
|
||||||
|
-DARDUINO_USB_MODE=1
|
||||||
|
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||||
|
|||||||
@ -8,20 +8,7 @@
|
|||||||
#include "PinMapping.h"
|
#include "PinMapping.h"
|
||||||
#include "SunPosition.h"
|
#include "SunPosition.h"
|
||||||
#include <Hoymiles.h>
|
#include <Hoymiles.h>
|
||||||
|
#include <SpiManager.h>
|
||||||
// the NRF shall use the second externally usable HW SPI controller
|
|
||||||
// for ESP32 that is the so-called VSPI, for ESP32-S2/S3 it is now called implicitly
|
|
||||||
// HSPI, as it has shifted places for these chip generations
|
|
||||||
// for all generations, this is equivalent to SPI3_HOST in the lower level driver
|
|
||||||
// For ESP32-C2, the only externally usable HW SPI controller is SPI2, its signal names
|
|
||||||
// being prefixed with FSPI.
|
|
||||||
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
|
|
||||||
#define SPI_NRF HSPI
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
|
||||||
#define SPI_NRF FSPI
|
|
||||||
#else
|
|
||||||
#define SPI_NRF VSPI
|
|
||||||
#endif
|
|
||||||
|
|
||||||
InverterSettingsClass InverterSettings;
|
InverterSettingsClass InverterSettings;
|
||||||
|
|
||||||
@ -44,7 +31,10 @@ void InverterSettingsClass::init(Scheduler& scheduler)
|
|||||||
|
|
||||||
if (PinMapping.isValidNrf24Config() || PinMapping.isValidCmt2300Config()) {
|
if (PinMapping.isValidNrf24Config() || PinMapping.isValidCmt2300Config()) {
|
||||||
if (PinMapping.isValidNrf24Config()) {
|
if (PinMapping.isValidNrf24Config()) {
|
||||||
SPIClass* spiClass = new SPIClass(SPI_NRF);
|
auto spi_bus = SpiManagerInst.claim_bus_arduino();
|
||||||
|
ESP_ERROR_CHECK(spi_bus ? ESP_OK : ESP_FAIL);
|
||||||
|
|
||||||
|
SPIClass* spiClass = new SPIClass(*spi_bus);
|
||||||
spiClass->begin(pin.nrf24_clk, pin.nrf24_miso, pin.nrf24_mosi, pin.nrf24_cs);
|
spiClass->begin(pin.nrf24_clk, pin.nrf24_miso, pin.nrf24_mosi, pin.nrf24_cs);
|
||||||
Hoymiles.initNRF(spiClass, pin.nrf24_en, pin.nrf24_irq);
|
Hoymiles.initNRF(spiClass, pin.nrf24_en, pin.nrf24_irq);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#include "MqttSettings.h"
|
#include "MqttSettings.h"
|
||||||
#include "NetworkSettings.h"
|
#include "NetworkSettings.h"
|
||||||
#include <Hoymiles.h>
|
#include <Hoymiles.h>
|
||||||
|
#include <CpuTemperature.h>
|
||||||
|
|
||||||
MqttHandleDtuClass MqttHandleDtu;
|
MqttHandleDtuClass MqttHandleDtu;
|
||||||
|
|
||||||
@ -34,8 +35,17 @@ void MqttHandleDtuClass::loop()
|
|||||||
MqttSettings.publish("dtu/uptime", String(millis() / 1000));
|
MqttSettings.publish("dtu/uptime", String(millis() / 1000));
|
||||||
MqttSettings.publish("dtu/ip", NetworkSettings.localIP().toString());
|
MqttSettings.publish("dtu/ip", NetworkSettings.localIP().toString());
|
||||||
MqttSettings.publish("dtu/hostname", NetworkSettings.getHostname());
|
MqttSettings.publish("dtu/hostname", NetworkSettings.getHostname());
|
||||||
|
MqttSettings.publish("dtu/heap/size", String(ESP.getHeapSize()));
|
||||||
|
MqttSettings.publish("dtu/heap/free", String(ESP.getFreeHeap()));
|
||||||
|
MqttSettings.publish("dtu/heap/minfree", String(ESP.getMinFreeHeap()));
|
||||||
|
MqttSettings.publish("dtu/heap/maxalloc", String(ESP.getMaxAllocHeap()));
|
||||||
if (NetworkSettings.NetworkMode() == network_mode::WiFi) {
|
if (NetworkSettings.NetworkMode() == network_mode::WiFi) {
|
||||||
MqttSettings.publish("dtu/rssi", String(WiFi.RSSI()));
|
MqttSettings.publish("dtu/rssi", String(WiFi.RSSI()));
|
||||||
MqttSettings.publish("dtu/bssid", WiFi.BSSIDstr());
|
MqttSettings.publish("dtu/bssid", WiFi.BSSIDstr());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float temperature = CpuTemperature.read();
|
||||||
|
if (!std::isnan(temperature)) {
|
||||||
|
MqttSettings.publish("dtu/temperature", String(temperature));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,8 +7,8 @@
|
|||||||
#include "MqttSettings.h"
|
#include "MqttSettings.h"
|
||||||
#include "NetworkSettings.h"
|
#include "NetworkSettings.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "defaults.h"
|
|
||||||
#include "__compiled_constants.h"
|
#include "__compiled_constants.h"
|
||||||
|
#include "defaults.h"
|
||||||
|
|
||||||
MqttHandleHassClass MqttHandleHass;
|
MqttHandleHassClass MqttHandleHass;
|
||||||
|
|
||||||
@ -58,29 +58,45 @@ void MqttHandleHassClass::publishConfig()
|
|||||||
const CONFIG_T& config = Configuration.get();
|
const CONFIG_T& config = Configuration.get();
|
||||||
|
|
||||||
// publish DTU sensors
|
// publish DTU sensors
|
||||||
publishDtuSensor("IP", "", "diagnostic", "mdi:network-outline", "", "");
|
publishDtuSensor("IP", "dtu/ip", "", "mdi:network-outline", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
publishDtuSensor("WiFi Signal", "signal_strength", "diagnostic", "", "dBm", "rssi");
|
publishDtuSensor("WiFi Signal", "dtu/rssi", "dBm", "", DEVICE_CLS_SIGNAL_STRENGTH, CATEGORY_DIAGNOSTIC);
|
||||||
publishDtuSensor("Uptime", "duration", "diagnostic", "", "s", "");
|
publishDtuSensor("Uptime", "dtu/uptime", "s", "", DEVICE_CLS_DURATION, CATEGORY_DIAGNOSTIC);
|
||||||
publishDtuBinarySensor("Status", "connectivity", "diagnostic", config.Mqtt.Lwt.Value_Online, config.Mqtt.Lwt.Value_Offline, config.Mqtt.Lwt.Topic);
|
publishDtuSensor("Temperature", "dtu/temperature", "°C", "mdi:thermometer", DEVICE_CLS_TEMPERATURE, CATEGORY_DIAGNOSTIC);
|
||||||
|
publishDtuSensor("Heap Size", "dtu/heap/size", "Bytes", "mdi:memory", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
|
publishDtuSensor("Heap Free", "dtu/heap/free", "Bytes", "mdi:memory", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
|
publishDtuSensor("Largest Free Heap Block", "dtu/heap/maxalloc", "Bytes", "mdi:memory", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
|
publishDtuSensor("Lifetime Minimum Free Heap", "dtu/heap/minfree", "Bytes", "mdi:memory", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
|
|
||||||
yield();
|
publishDtuSensor("Yield Total", "ac/yieldtotal", "kWh", "", DEVICE_CLS_ENERGY, CATEGORY_NONE);
|
||||||
|
publishDtuSensor("Yield Day", "ac/yieldday", "Wh", "", DEVICE_CLS_ENERGY, CATEGORY_NONE);
|
||||||
|
publishDtuSensor("AC Power", "ac/power", "W", "", DEVICE_CLS_PWR, CATEGORY_NONE);
|
||||||
|
|
||||||
|
publishDtuBinarySensor("Status", config.Mqtt.Lwt.Topic, config.Mqtt.Lwt.Value_Online, config.Mqtt.Lwt.Value_Offline, DEVICE_CLS_CONNECTIVITY, CATEGORY_DIAGNOSTIC);
|
||||||
|
|
||||||
// Loop all inverters
|
// Loop all inverters
|
||||||
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
|
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
|
||||||
auto inv = Hoymiles.getInverterByPos(i);
|
auto inv = Hoymiles.getInverterByPos(i);
|
||||||
|
|
||||||
publishInverterButton(inv, "Turn Inverter Off", "mdi:power-plug-off", "config", "", "cmd/power", "0");
|
publishInverterButton(inv, "Turn Inverter Off", "cmd/power", "0", "mdi:power-plug-off", DEVICE_CLS_NONE, CATEGORY_CONFIG);
|
||||||
publishInverterButton(inv, "Turn Inverter On", "mdi:power-plug", "config", "", "cmd/power", "1");
|
publishInverterButton(inv, "Turn Inverter On", "cmd/power", "1", "mdi:power-plug", DEVICE_CLS_NONE, CATEGORY_CONFIG);
|
||||||
publishInverterButton(inv, "Restart Inverter", "", "config", "restart", "cmd/restart", "1");
|
publishInverterButton(inv, "Restart Inverter", "cmd/restart", "1", "", DEVICE_CLS_RESTART, CATEGORY_CONFIG);
|
||||||
|
publishInverterButton(inv, "Reset Radio Statistics", "cmd/reset_rf_stats", "1", "", DEVICE_CLS_NONE, CATEGORY_CONFIG);
|
||||||
|
|
||||||
publishInverterNumber(inv, "Limit NonPersistent Relative", "mdi:speedometer", "config", "cmd/limit_nonpersistent_relative", "status/limit_relative", "%", 0, 100, 0.1);
|
publishInverterNumber(inv, "Limit NonPersistent Relative", "status/limit_relative", "cmd/limit_nonpersistent_relative", 0, 100, 0.1, "%", "mdi:speedometer", CATEGORY_CONFIG);
|
||||||
publishInverterNumber(inv, "Limit Persistent Relative", "mdi:speedometer", "config", "cmd/limit_persistent_relative", "status/limit_relative", "%", 0, 100, 0.1);
|
publishInverterNumber(inv, "Limit Persistent Relative", "status/limit_relative", "cmd/limit_persistent_relative", 0, 100, 0.1, "%", "mdi:speedometer", CATEGORY_CONFIG);
|
||||||
|
|
||||||
publishInverterNumber(inv, "Limit NonPersistent Absolute", "mdi:speedometer", "config", "cmd/limit_nonpersistent_absolute", "status/limit_absolute", "W", 0, MAX_INVERTER_LIMIT);
|
publishInverterNumber(inv, "Limit NonPersistent Absolute", "status/limit_absolute", "cmd/limit_nonpersistent_absolute", 0, MAX_INVERTER_LIMIT, 1, "W", "mdi:speedometer", CATEGORY_CONFIG);
|
||||||
publishInverterNumber(inv, "Limit Persistent Absolute", "mdi:speedometer", "config", "cmd/limit_persistent_absolute", "status/limit_absolute", "W", 0, MAX_INVERTER_LIMIT);
|
publishInverterNumber(inv, "Limit Persistent Absolute", "status/limit_absolute", "cmd/limit_persistent_absolute", 0, MAX_INVERTER_LIMIT, 1, "W", "mdi:speedometer", CATEGORY_CONFIG);
|
||||||
|
|
||||||
publishInverterBinarySensor(inv, "Reachable", "status/reachable", "1", "0");
|
publishInverterBinarySensor(inv, "Reachable", "status/reachable", "1", "0", DEVICE_CLS_CONNECTIVITY, CATEGORY_DIAGNOSTIC);
|
||||||
publishInverterBinarySensor(inv, "Producing", "status/producing", "1", "0");
|
publishInverterBinarySensor(inv, "Producing", "status/producing", "1", "0", DEVICE_CLS_NONE, CATEGORY_NONE);
|
||||||
|
|
||||||
|
publishInverterSensor(inv, "TX Requests", "radio/tx_request", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
|
publishInverterSensor(inv, "RX Success", "radio/rx_success", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
|
publishInverterSensor(inv, "RX Fail Receive Nothing", "radio/rx_fail_nothing", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
|
publishInverterSensor(inv, "RX Fail Receive Partial", "radio/rx_fail_partial", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
|
publishInverterSensor(inv, "RX Fail Receive Corrupt", "radio/rx_fail_corrupt", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
|
publishInverterSensor(inv, "TX Re-Request Fragment", "radio/tx_re_request", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC);
|
||||||
|
|
||||||
// Loop all channels
|
// Loop all channels
|
||||||
for (auto& t : inv->Statistics()->getChannelTypes()) {
|
for (auto& t : inv->Statistics()->getChannelTypes()) {
|
||||||
@ -94,8 +110,6 @@ void MqttHandleHassClass::publishConfig()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
yield();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,8 +142,7 @@ void MqttHandleHassClass::publishInverterField(std::shared_ptr<InverterAbstract>
|
|||||||
|
|
||||||
if (!clear) {
|
if (!clear) {
|
||||||
const String stateTopic = MqttSettings.getPrefix() + MqttHandleInverter.getTopic(inv, type, channel, fieldType.fieldId);
|
const String stateTopic = MqttSettings.getPrefix() + MqttHandleInverter.getTopic(inv, type, channel, fieldType.fieldId);
|
||||||
const char* devCls = deviceClasses[fieldType.deviceClsId];
|
const char* stateCls = stateClass_name[fieldType.stateClsId];
|
||||||
const char* stateCls = stateClasses[fieldType.stateClsId];
|
|
||||||
|
|
||||||
String name;
|
String name;
|
||||||
if (type != TYPE_DC) {
|
if (type != TYPE_DC) {
|
||||||
@ -138,46 +151,34 @@ void MqttHandleHassClass::publishInverterField(std::shared_ptr<InverterAbstract>
|
|||||||
name = "CH" + chanNum + " " + fieldName;
|
name = "CH" + chanNum + " " + fieldName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String unit_of_measure = inv->Statistics()->getChannelFieldUnit(type, channel, fieldType.fieldId);
|
||||||
|
|
||||||
JsonDocument root;
|
JsonDocument root;
|
||||||
|
createInverterInfo(root, inv);
|
||||||
|
addCommonMetadata(root, unit_of_measure, "", fieldType.deviceClsId, CATEGORY_NONE);
|
||||||
|
|
||||||
root["name"] = name;
|
root["name"] = name;
|
||||||
root["stat_t"] = stateTopic;
|
root["stat_t"] = stateTopic;
|
||||||
root["uniq_id"] = serial + "_ch" + chanNum + "_" + fieldName;
|
root["uniq_id"] = serial + "_ch" + chanNum + "_" + fieldName;
|
||||||
|
|
||||||
String unit_of_measure = inv->Statistics()->getChannelFieldUnit(type, channel, fieldType.fieldId);
|
|
||||||
if (unit_of_measure != "") {
|
|
||||||
root["unit_of_meas"] = unit_of_measure;
|
|
||||||
}
|
|
||||||
|
|
||||||
createInverterInfo(root, inv);
|
|
||||||
|
|
||||||
if (Configuration.get().Mqtt.Hass.Expire) {
|
if (Configuration.get().Mqtt.Hass.Expire) {
|
||||||
root["exp_aft"] = Hoymiles.getNumInverters() * max<uint32_t>(Hoymiles.PollInterval(), Configuration.get().Mqtt.PublishInterval) * inv->getReachableThreshold();
|
root["exp_aft"] = Hoymiles.getNumInverters() * max<uint32_t>(Hoymiles.PollInterval(), Configuration.get().Mqtt.PublishInterval) * inv->getReachableThreshold();
|
||||||
}
|
}
|
||||||
if (devCls != 0) {
|
|
||||||
root["dev_cla"] = devCls;
|
|
||||||
}
|
|
||||||
if (stateCls != 0) {
|
if (stateCls != 0) {
|
||||||
root["stat_cla"] = stateCls;
|
root["stat_cla"] = stateCls;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
publish(configTopic, root);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String buffer;
|
|
||||||
serializeJson(root, buffer);
|
|
||||||
publish(configTopic, buffer);
|
|
||||||
} else {
|
} else {
|
||||||
publish(configTopic, "");
|
publish(configTopic, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttHandleHassClass::publishInverterButton(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category, const char* deviceClass, const char* subTopic, const char* payload)
|
void MqttHandleHassClass::publishInverterButton(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& payload, const String& icon, const DeviceClassType device_class, const CategoryType category)
|
||||||
{
|
{
|
||||||
const String serial = inv->serialString();
|
const String serial = inv->serialString();
|
||||||
|
|
||||||
String buttonId = caption;
|
String buttonId = name;
|
||||||
buttonId.replace(" ", "_");
|
buttonId.replace(" ", "_");
|
||||||
buttonId.toLowerCase();
|
buttonId.toLowerCase();
|
||||||
|
|
||||||
@ -185,41 +186,29 @@ void MqttHandleHassClass::publishInverterButton(std::shared_ptr<InverterAbstract
|
|||||||
+ "/" + buttonId
|
+ "/" + buttonId
|
||||||
+ "/config";
|
+ "/config";
|
||||||
|
|
||||||
const String cmdTopic = MqttSettings.getPrefix() + serial + "/" + subTopic;
|
const String cmdTopic = MqttSettings.getPrefix() + serial + "/" + state_topic;
|
||||||
|
|
||||||
JsonDocument root;
|
JsonDocument root;
|
||||||
|
createInverterInfo(root, inv);
|
||||||
|
addCommonMetadata(root, "", icon, device_class, category);
|
||||||
|
|
||||||
root["name"] = caption;
|
root["name"] = name;
|
||||||
root["uniq_id"] = serial + "_" + buttonId;
|
root["uniq_id"] = serial + "_" + buttonId;
|
||||||
if (strcmp(icon, "")) {
|
|
||||||
root["ic"] = icon;
|
|
||||||
}
|
|
||||||
if (strcmp(deviceClass, "")) {
|
|
||||||
root["dev_cla"] = deviceClass;
|
|
||||||
}
|
|
||||||
root["ent_cat"] = category;
|
|
||||||
root["cmd_t"] = cmdTopic;
|
root["cmd_t"] = cmdTopic;
|
||||||
root["payload_press"] = payload;
|
root["payload_press"] = payload;
|
||||||
|
|
||||||
createInverterInfo(root, inv);
|
publish(configTopic, root);
|
||||||
|
|
||||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String buffer;
|
|
||||||
serializeJson(root, buffer);
|
|
||||||
publish(configTopic, buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttHandleHassClass::publishInverterNumber(
|
void MqttHandleHassClass::publishInverterNumber(
|
||||||
std::shared_ptr<InverterAbstract> inv, const char* caption, const char* icon, const char* category,
|
std::shared_ptr<InverterAbstract> inv, const String& name,
|
||||||
const char* commandTopic, const char* stateTopic, const char* unitOfMeasure,
|
const String& stateTopic, const String& command_topic,
|
||||||
const int16_t min, const int16_t max, float step)
|
const int16_t min, const int16_t max, float step,
|
||||||
|
const String& unit_of_measure, const String& icon, const CategoryType category)
|
||||||
{
|
{
|
||||||
const String serial = inv->serialString();
|
const String serial = inv->serialString();
|
||||||
|
|
||||||
String buttonId = caption;
|
String buttonId = name;
|
||||||
buttonId.replace(" ", "_");
|
buttonId.replace(" ", "_");
|
||||||
buttonId.toLowerCase();
|
buttonId.toLowerCase();
|
||||||
|
|
||||||
@ -227,150 +216,22 @@ void MqttHandleHassClass::publishInverterNumber(
|
|||||||
+ "/" + buttonId
|
+ "/" + buttonId
|
||||||
+ "/config";
|
+ "/config";
|
||||||
|
|
||||||
const String cmdTopic = MqttSettings.getPrefix() + serial + "/" + commandTopic;
|
const String cmdTopic = MqttSettings.getPrefix() + serial + "/" + command_topic;
|
||||||
const String statTopic = MqttSettings.getPrefix() + serial + "/" + stateTopic;
|
const String statTopic = MqttSettings.getPrefix() + serial + "/" + stateTopic;
|
||||||
|
|
||||||
JsonDocument root;
|
JsonDocument root;
|
||||||
|
createInverterInfo(root, inv);
|
||||||
|
addCommonMetadata(root, unit_of_measure, icon, DEVICE_CLS_NONE, category);
|
||||||
|
|
||||||
root["name"] = caption;
|
root["name"] = name;
|
||||||
root["uniq_id"] = serial + "_" + buttonId;
|
root["uniq_id"] = serial + "_" + buttonId;
|
||||||
if (strcmp(icon, "")) {
|
|
||||||
root["ic"] = icon;
|
|
||||||
}
|
|
||||||
root["ent_cat"] = category;
|
|
||||||
root["cmd_t"] = cmdTopic;
|
root["cmd_t"] = cmdTopic;
|
||||||
root["stat_t"] = statTopic;
|
root["stat_t"] = statTopic;
|
||||||
root["unit_of_meas"] = unitOfMeasure;
|
|
||||||
root["min"] = min;
|
root["min"] = min;
|
||||||
root["max"] = max;
|
root["max"] = max;
|
||||||
root["step"] = step;
|
root["step"] = step;
|
||||||
|
|
||||||
createInverterInfo(root, inv);
|
publish(configTopic, root);
|
||||||
|
|
||||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String buffer;
|
|
||||||
serializeJson(root, buffer);
|
|
||||||
publish(configTopic, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MqttHandleHassClass::publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const char* caption, const char* subTopic, const char* payload_on, const char* payload_off)
|
|
||||||
{
|
|
||||||
const String serial = inv->serialString();
|
|
||||||
|
|
||||||
String sensorId = caption;
|
|
||||||
sensorId.replace(" ", "_");
|
|
||||||
sensorId.toLowerCase();
|
|
||||||
|
|
||||||
const String configTopic = "binary_sensor/dtu_" + serial
|
|
||||||
+ "/" + sensorId
|
|
||||||
+ "/config";
|
|
||||||
|
|
||||||
const String statTopic = MqttSettings.getPrefix() + serial + "/" + subTopic;
|
|
||||||
|
|
||||||
JsonDocument root;
|
|
||||||
|
|
||||||
root["name"] = caption;
|
|
||||||
root["uniq_id"] = serial + "_" + sensorId;
|
|
||||||
root["stat_t"] = statTopic;
|
|
||||||
root["pl_on"] = payload_on;
|
|
||||||
root["pl_off"] = payload_off;
|
|
||||||
|
|
||||||
createInverterInfo(root, inv);
|
|
||||||
|
|
||||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String buffer;
|
|
||||||
serializeJson(root, buffer);
|
|
||||||
publish(configTopic, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MqttHandleHassClass::publishDtuSensor(const char* name, const char* device_class, const char* category, const char* icon, const char* unit_of_measure, const char* subTopic)
|
|
||||||
{
|
|
||||||
String id = name;
|
|
||||||
id.toLowerCase();
|
|
||||||
id.replace(" ", "_");
|
|
||||||
String topic = subTopic;
|
|
||||||
if (topic == "") {
|
|
||||||
topic = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonDocument root;
|
|
||||||
|
|
||||||
root["name"] = name;
|
|
||||||
root["uniq_id"] = getDtuUniqueId() + "_" + id;
|
|
||||||
if (strcmp(device_class, "")) {
|
|
||||||
root["dev_cla"] = device_class;
|
|
||||||
}
|
|
||||||
if (strcmp(category, "")) {
|
|
||||||
root["ent_cat"] = category;
|
|
||||||
}
|
|
||||||
if (strcmp(icon, "")) {
|
|
||||||
root["ic"] = icon;
|
|
||||||
}
|
|
||||||
if (strcmp(unit_of_measure, "")) {
|
|
||||||
root["unit_of_meas"] = unit_of_measure;
|
|
||||||
}
|
|
||||||
root["stat_t"] = MqttSettings.getPrefix() + "dtu" + "/" + topic;
|
|
||||||
|
|
||||||
root["avty_t"] = MqttSettings.getPrefix() + Configuration.get().Mqtt.Lwt.Topic;
|
|
||||||
|
|
||||||
const CONFIG_T& config = Configuration.get();
|
|
||||||
root["pl_avail"] = config.Mqtt.Lwt.Value_Online;
|
|
||||||
root["pl_not_avail"] = config.Mqtt.Lwt.Value_Offline;
|
|
||||||
|
|
||||||
createDtuInfo(root);
|
|
||||||
|
|
||||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String buffer;
|
|
||||||
const String configTopic = "sensor/" + getDtuUniqueId() + "/" + id + "/config";
|
|
||||||
serializeJson(root, buffer);
|
|
||||||
publish(configTopic, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MqttHandleHassClass::publishDtuBinarySensor(const char* name, const char* device_class, const char* category, const char* payload_on, const char* payload_off, const char* subTopic)
|
|
||||||
{
|
|
||||||
String id = name;
|
|
||||||
id.toLowerCase();
|
|
||||||
id.replace(" ", "_");
|
|
||||||
|
|
||||||
String topic = subTopic;
|
|
||||||
if (!strcmp(subTopic, "")) {
|
|
||||||
topic = String("dtu/") + "/" + id;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonDocument root;
|
|
||||||
|
|
||||||
root["name"] = name;
|
|
||||||
root["uniq_id"] = getDtuUniqueId() + "_" + id;
|
|
||||||
root["stat_t"] = MqttSettings.getPrefix() + topic;
|
|
||||||
root["pl_on"] = payload_on;
|
|
||||||
root["pl_off"] = payload_off;
|
|
||||||
|
|
||||||
if (strcmp(device_class, "")) {
|
|
||||||
root["dev_cla"] = device_class;
|
|
||||||
}
|
|
||||||
if (strcmp(category, "")) {
|
|
||||||
root["ent_cat"] = category;
|
|
||||||
}
|
|
||||||
|
|
||||||
createDtuInfo(root);
|
|
||||||
|
|
||||||
if (!Utils::checkJsonAlloc(root, __FUNCTION__, __LINE__)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String buffer;
|
|
||||||
const String configTopic = "binary_sensor/" + getDtuUniqueId() + "/" + id + "/config";
|
|
||||||
serializeJson(root, buffer);
|
|
||||||
publish(configTopic, buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttHandleHassClass::createInverterInfo(JsonDocument& root, std::shared_ptr<InverterAbstract> inv)
|
void MqttHandleHassClass::createInverterInfo(JsonDocument& root, std::shared_ptr<InverterAbstract> inv)
|
||||||
@ -433,4 +294,106 @@ void MqttHandleHassClass::publish(const String& subtopic, const String& payload)
|
|||||||
String topic = Configuration.get().Mqtt.Hass.Topic;
|
String topic = Configuration.get().Mqtt.Hass.Topic;
|
||||||
topic += subtopic;
|
topic += subtopic;
|
||||||
MqttSettings.publishGeneric(topic, payload, Configuration.get().Mqtt.Hass.Retain);
|
MqttSettings.publishGeneric(topic, payload, Configuration.get().Mqtt.Hass.Retain);
|
||||||
|
yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::publish(const String& subtopic, const JsonDocument& doc)
|
||||||
|
{
|
||||||
|
if (!Utils::checkJsonAlloc(doc, __FUNCTION__, __LINE__)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String buffer;
|
||||||
|
serializeJson(doc, buffer);
|
||||||
|
publish(subtopic, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::addCommonMetadata(JsonDocument& doc, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category)
|
||||||
|
{
|
||||||
|
if (unit_of_measure != "") {
|
||||||
|
doc["unit_of_meas"] = unit_of_measure;
|
||||||
|
}
|
||||||
|
if (icon != "") {
|
||||||
|
doc["ic"] = icon;
|
||||||
|
}
|
||||||
|
if (device_class != DEVICE_CLS_NONE) {
|
||||||
|
doc["dev_cla"] = deviceClass_name[device_class];
|
||||||
|
}
|
||||||
|
if (category != CATEGORY_NONE) {
|
||||||
|
doc["ent_cat"] = category_name[category];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::publishBinarySensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category)
|
||||||
|
{
|
||||||
|
String sensor_id = name;
|
||||||
|
sensor_id.toLowerCase();
|
||||||
|
sensor_id.replace(" ", "_");
|
||||||
|
|
||||||
|
doc["name"] = name;
|
||||||
|
doc["uniq_id"] = unique_id_prefix + "_" + sensor_id;
|
||||||
|
doc["stat_t"] = MqttSettings.getPrefix() + state_topic;
|
||||||
|
doc["pl_on"] = payload_on;
|
||||||
|
doc["pl_off"] = payload_off;
|
||||||
|
|
||||||
|
addCommonMetadata(doc, "", "", device_class, category);
|
||||||
|
|
||||||
|
const String configTopic = "binary_sensor/" + root_device + "/" + sensor_id + "/config";
|
||||||
|
publish(configTopic, doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::publishDtuBinarySensor(const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category)
|
||||||
|
{
|
||||||
|
const String dtuId = getDtuUniqueId();
|
||||||
|
|
||||||
|
JsonDocument root;
|
||||||
|
createDtuInfo(root);
|
||||||
|
publishBinarySensor(root, dtuId, dtuId, name, state_topic, payload_on, payload_off, device_class, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::publishInverterBinarySensor(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category)
|
||||||
|
{
|
||||||
|
const String serial = inv->serialString();
|
||||||
|
|
||||||
|
JsonDocument root;
|
||||||
|
createInverterInfo(root, inv);
|
||||||
|
publishBinarySensor(root, "dtu_" + serial, serial, name, serial + "/" + state_topic, payload_on, payload_off, device_class, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::publishSensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category)
|
||||||
|
{
|
||||||
|
String sensor_id = name;
|
||||||
|
sensor_id.toLowerCase();
|
||||||
|
sensor_id.replace(" ", "_");
|
||||||
|
|
||||||
|
doc["name"] = name;
|
||||||
|
doc["uniq_id"] = unique_id_prefix + "_" + sensor_id;
|
||||||
|
doc["stat_t"] = MqttSettings.getPrefix() + state_topic;
|
||||||
|
|
||||||
|
addCommonMetadata(doc, unit_of_measure, icon, device_class, category);
|
||||||
|
|
||||||
|
const CONFIG_T& config = Configuration.get();
|
||||||
|
doc["avty_t"] = MqttSettings.getPrefix() + config.Mqtt.Lwt.Topic;
|
||||||
|
doc["pl_avail"] = config.Mqtt.Lwt.Value_Online;
|
||||||
|
doc["pl_not_avail"] = config.Mqtt.Lwt.Value_Offline;
|
||||||
|
|
||||||
|
const String configTopic = "sensor/" + root_device + "/" + sensor_id + "/config";
|
||||||
|
publish(configTopic, doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::publishDtuSensor(const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category)
|
||||||
|
{
|
||||||
|
const String dtuId = getDtuUniqueId();
|
||||||
|
|
||||||
|
JsonDocument root;
|
||||||
|
createDtuInfo(root);
|
||||||
|
publishSensor(root, dtuId, dtuId, name, state_topic, unit_of_measure, icon, device_class, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MqttHandleHassClass::publishInverterSensor(std::shared_ptr<InverterAbstract> inv, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category)
|
||||||
|
{
|
||||||
|
const String serial = inv->serialString();
|
||||||
|
|
||||||
|
JsonDocument root;
|
||||||
|
createInverterInfo(root, inv);
|
||||||
|
publishSensor(root, "dtu_" + serial, serial, name, serial + "/" + state_topic, unit_of_measure, icon, device_class, category);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,13 +7,6 @@
|
|||||||
#include "MqttSettings.h"
|
#include "MqttSettings.h"
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
#define TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE "limit_persistent_relative"
|
|
||||||
#define TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE "limit_persistent_absolute"
|
|
||||||
#define TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE "limit_nonpersistent_relative"
|
|
||||||
#define TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE "limit_nonpersistent_absolute"
|
|
||||||
#define TOPIC_SUB_POWER "power"
|
|
||||||
#define TOPIC_SUB_RESTART "restart"
|
|
||||||
|
|
||||||
#define PUBLISH_MAX_INTERVAL 60000
|
#define PUBLISH_MAX_INTERVAL 60000
|
||||||
|
|
||||||
MqttHandleInverterClass MqttHandleInverter;
|
MqttHandleInverterClass MqttHandleInverter;
|
||||||
@ -50,6 +43,14 @@ void MqttHandleInverterClass::loop()
|
|||||||
// Name
|
// Name
|
||||||
MqttSettings.publish(subtopic + "/name", inv->name());
|
MqttSettings.publish(subtopic + "/name", inv->name());
|
||||||
|
|
||||||
|
// Radio Statistics
|
||||||
|
MqttSettings.publish(subtopic + "/radio/tx_request", String(inv->RadioStats.TxRequestData));
|
||||||
|
MqttSettings.publish(subtopic + "/radio/tx_re_request", String(inv->RadioStats.TxReRequestFragment));
|
||||||
|
MqttSettings.publish(subtopic + "/radio/rx_success", String(inv->RadioStats.RxSuccess));
|
||||||
|
MqttSettings.publish(subtopic + "/radio/rx_fail_nothing", String(inv->RadioStats.RxFailNoAnswer));
|
||||||
|
MqttSettings.publish(subtopic + "/radio/rx_fail_partial", String(inv->RadioStats.RxFailPartialAnswer));
|
||||||
|
MqttSettings.publish(subtopic + "/radio/rx_fail_corrupt", String(inv->RadioStats.RxFailCorruptData));
|
||||||
|
|
||||||
if (inv->DevInfo()->getLastUpdate() > 0) {
|
if (inv->DevInfo()->getLastUpdate() > 0) {
|
||||||
// Bootloader Version
|
// Bootloader Version
|
||||||
MqttSettings.publish(subtopic + "/device/bootloaderversion", String(inv->DevInfo()->getFwBootloaderVersion()));
|
MqttSettings.publish(subtopic + "/device/bootloaderversion", String(inv->DevInfo()->getFwBootloaderVersion()));
|
||||||
@ -146,7 +147,7 @@ String MqttHandleInverterClass::getTopic(std::shared_ptr<InverterAbstract> inv,
|
|||||||
return inv->serialString() + "/" + chanNum + "/" + chanName;
|
return inv->serialString() + "/" + chanNum + "/" + chanName;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, const size_t len, const size_t index, const size_t total)
|
void MqttHandleInverterClass::onMqttMessage(Topic t, const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, const size_t len, const size_t index, const size_t total)
|
||||||
{
|
{
|
||||||
const CONFIG_T& config = Configuration.get();
|
const CONFIG_T& config = Configuration.get();
|
||||||
|
|
||||||
@ -154,15 +155,11 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
|||||||
strncpy(token_topic, topic, MQTT_MAX_TOPIC_STRLEN + 40); // convert const char* to char*
|
strncpy(token_topic, topic, MQTT_MAX_TOPIC_STRLEN + 40); // convert const char* to char*
|
||||||
|
|
||||||
char* serial_str;
|
char* serial_str;
|
||||||
char* subtopic;
|
|
||||||
char* setting;
|
|
||||||
char* rest = &token_topic[strlen(config.Mqtt.Topic)];
|
char* rest = &token_topic[strlen(config.Mqtt.Topic)];
|
||||||
|
|
||||||
serial_str = strtok_r(rest, "/", &rest);
|
serial_str = strtok_r(rest, "/", &rest);
|
||||||
subtopic = strtok_r(rest, "/", &rest);
|
|
||||||
setting = strtok_r(rest, "/", &rest);
|
|
||||||
|
|
||||||
if (serial_str == NULL || subtopic == NULL || setting == NULL) {
|
if (serial_str == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,33 +172,30 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if subtopic is unequal cmd
|
std::string strValue(reinterpret_cast<const char*>(payload), len);
|
||||||
if (strcmp(subtopic, "cmd")) {
|
float payload_val = -1;
|
||||||
|
try {
|
||||||
|
payload_val = std::stof(strValue);
|
||||||
|
} catch (std::invalid_argument const& e) {
|
||||||
|
MessageOutput.printf("MQTT handler: cannot parse payload of topic '%s' as float: %s\r\n",
|
||||||
|
topic, strValue.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* strlimit = new char[len + 1];
|
switch (t) {
|
||||||
memcpy(strlimit, payload, len);
|
case Topic::LimitPersistentRelative:
|
||||||
strlimit[len] = '\0';
|
|
||||||
const float payload_val = strtof(strlimit, NULL);
|
|
||||||
delete[] strlimit;
|
|
||||||
|
|
||||||
if (payload_val < 0) {
|
|
||||||
MessageOutput.printf("MQTT payload < 0 received --> ignoring\r\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(setting, TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE)) {
|
|
||||||
// Set inverter limit relative persistent
|
// Set inverter limit relative persistent
|
||||||
MessageOutput.printf("Limit Persistent: %.1f %%\r\n", payload_val);
|
MessageOutput.printf("Limit Persistent: %.1f %%\r\n", payload_val);
|
||||||
inv->sendActivePowerControlRequest(payload_val, PowerLimitControlType::RelativPersistent);
|
inv->sendActivePowerControlRequest(payload_val, PowerLimitControlType::RelativPersistent);
|
||||||
|
break;
|
||||||
|
|
||||||
} else if (!strcmp(setting, TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE)) {
|
case Topic::LimitPersistentAbsolute:
|
||||||
// Set inverter limit absolute persistent
|
// Set inverter limit absolute persistent
|
||||||
MessageOutput.printf("Limit Persistent: %.1f W\r\n", payload_val);
|
MessageOutput.printf("Limit Persistent: %.1f W\r\n", payload_val);
|
||||||
inv->sendActivePowerControlRequest(payload_val, PowerLimitControlType::AbsolutPersistent);
|
inv->sendActivePowerControlRequest(payload_val, PowerLimitControlType::AbsolutPersistent);
|
||||||
|
break;
|
||||||
|
|
||||||
} else if (!strcmp(setting, TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE)) {
|
case Topic::LimitNonPersistentRelative:
|
||||||
// Set inverter limit relative non persistent
|
// Set inverter limit relative non persistent
|
||||||
MessageOutput.printf("Limit Non-Persistent: %.1f %%\r\n", payload_val);
|
MessageOutput.printf("Limit Non-Persistent: %.1f %%\r\n", payload_val);
|
||||||
if (!properties.retain) {
|
if (!properties.retain) {
|
||||||
@ -209,8 +203,9 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
|||||||
} else {
|
} else {
|
||||||
MessageOutput.println("Ignored because retained");
|
MessageOutput.println("Ignored because retained");
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
} else if (!strcmp(setting, TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE)) {
|
case Topic::LimitNonPersistentAbsolute:
|
||||||
// Set inverter limit absolute non persistent
|
// Set inverter limit absolute non persistent
|
||||||
MessageOutput.printf("Limit Non-Persistent: %.1f W\r\n", payload_val);
|
MessageOutput.printf("Limit Non-Persistent: %.1f W\r\n", payload_val);
|
||||||
if (!properties.retain) {
|
if (!properties.retain) {
|
||||||
@ -218,13 +213,15 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
|||||||
} else {
|
} else {
|
||||||
MessageOutput.println("Ignored because retained");
|
MessageOutput.println("Ignored because retained");
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
} else if (!strcmp(setting, TOPIC_SUB_POWER)) {
|
case Topic::Power:
|
||||||
// Turn inverter on or off
|
// Turn inverter on or off
|
||||||
MessageOutput.printf("Set inverter power to: %d\r\n", static_cast<int32_t>(payload_val));
|
MessageOutput.printf("Set inverter power to: %d\r\n", static_cast<int32_t>(payload_val));
|
||||||
inv->sendPowerControlRequest(static_cast<int32_t>(payload_val) > 0);
|
inv->sendPowerControlRequest(static_cast<int32_t>(payload_val) > 0);
|
||||||
|
break;
|
||||||
|
|
||||||
} else if (!strcmp(setting, TOPIC_SUB_RESTART)) {
|
case Topic::Restart:
|
||||||
// Restart inverter
|
// Restart inverter
|
||||||
MessageOutput.printf("Restart inverter\r\n");
|
MessageOutput.printf("Restart inverter\r\n");
|
||||||
if (!properties.retain && payload_val == 1) {
|
if (!properties.retain && payload_val == 1) {
|
||||||
@ -232,34 +229,41 @@ void MqttHandleInverterClass::onMqttMessage(const espMqttClientTypes::MessagePro
|
|||||||
} else {
|
} else {
|
||||||
MessageOutput.println("Ignored because retained or numeric value not '1'");
|
MessageOutput.println("Ignored because retained or numeric value not '1'");
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Topic::ResetRfStats:
|
||||||
|
// Reset RF Stats
|
||||||
|
MessageOutput.printf("Reset RF stats\r\n");
|
||||||
|
if (!properties.retain && payload_val == 1) {
|
||||||
|
inv->resetRadioStats();
|
||||||
|
} else {
|
||||||
|
MessageOutput.println("Ignored because retained or numeric value not '1'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttHandleInverterClass::subscribeTopics()
|
void MqttHandleInverterClass::subscribeTopics()
|
||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
String const& prefix = MqttSettings.getPrefix();
|
||||||
using std::placeholders::_2;
|
|
||||||
using std::placeholders::_3;
|
|
||||||
using std::placeholders::_4;
|
|
||||||
using std::placeholders::_5;
|
|
||||||
using std::placeholders::_6;
|
|
||||||
|
|
||||||
const String topic = MqttSettings.getPrefix();
|
auto subscribe = [&prefix, this](char const* subTopic, Topic t) {
|
||||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
String fullTopic(prefix + _cmdtopic.data() + subTopic);
|
||||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
MqttSettings.subscribe(fullTopic.c_str(), 0,
|
||||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
std::bind(&MqttHandleInverterClass::onMqttMessage, this, t,
|
||||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
std::placeholders::_1, std::placeholders::_2,
|
||||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_POWER), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
std::placeholders::_3, std::placeholders::_4,
|
||||||
MqttSettings.subscribe(String(topic + "+/cmd/" + TOPIC_SUB_RESTART), 0, std::bind(&MqttHandleInverterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
std::placeholders::_5, std::placeholders::_6));
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto const& s : _subscriptions) {
|
||||||
|
subscribe(s.first.data(), s.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MqttHandleInverterClass::unsubscribeTopics()
|
void MqttHandleInverterClass::unsubscribeTopics()
|
||||||
{
|
{
|
||||||
const String topic = MqttSettings.getPrefix();
|
String const& prefix = MqttSettings.getPrefix() + _cmdtopic.data();
|
||||||
MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_RELATIVE));
|
for (auto const& s : _subscriptions) {
|
||||||
MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_PERSISTENT_ABSOLUTE));
|
MqttSettings.unsubscribe(prefix + s.first.data());
|
||||||
MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_RELATIVE));
|
}
|
||||||
MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_LIMIT_NONPERSISTENT_ABSOLUTE));
|
|
||||||
MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_POWER));
|
|
||||||
MqttSettings.unsubscribe(String(topic + "+/cmd/" + TOPIC_SUB_RESTART));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,10 +7,10 @@
|
|||||||
#include "MessageOutput.h"
|
#include "MessageOutput.h"
|
||||||
#include "PinMapping.h"
|
#include "PinMapping.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
#include "__compiled_constants.h"
|
||||||
#include "defaults.h"
|
#include "defaults.h"
|
||||||
#include <ESPmDNS.h>
|
#include <ESPmDNS.h>
|
||||||
#include <ETH.h>
|
#include <ETH.h>
|
||||||
#include "__compiled_constants.h"
|
|
||||||
|
|
||||||
NetworkSettingsClass::NetworkSettingsClass()
|
NetworkSettingsClass::NetworkSettingsClass()
|
||||||
: _loopTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&NetworkSettingsClass::loop, this))
|
: _loopTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&NetworkSettingsClass::loop, this))
|
||||||
@ -23,20 +23,34 @@ NetworkSettingsClass::NetworkSettingsClass()
|
|||||||
void NetworkSettingsClass::init(Scheduler& scheduler)
|
void NetworkSettingsClass::init(Scheduler& scheduler)
|
||||||
{
|
{
|
||||||
using std::placeholders::_1;
|
using std::placeholders::_1;
|
||||||
|
using std::placeholders::_2;
|
||||||
|
|
||||||
WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN);
|
WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN);
|
||||||
WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL);
|
WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL);
|
||||||
|
|
||||||
WiFi.disconnect(true, true);
|
WiFi.disconnect(true, true);
|
||||||
|
|
||||||
WiFi.onEvent(std::bind(&NetworkSettingsClass::NetworkEvent, this, _1));
|
WiFi.onEvent(std::bind(&NetworkSettingsClass::NetworkEvent, this, _1, _2));
|
||||||
|
|
||||||
|
if (PinMapping.isValidW5500Config()) {
|
||||||
|
PinMapping_t& pin = PinMapping.get();
|
||||||
|
_w5500 = std::make_unique<W5500>(pin.w5500_mosi, pin.w5500_miso, pin.w5500_sclk, pin.w5500_cs, pin.w5500_int, pin.w5500_rst);
|
||||||
|
} else if (PinMapping.isValidEthConfig()) {
|
||||||
|
PinMapping_t& pin = PinMapping.get();
|
||||||
|
#if ESP_ARDUINO_VERSION_MAJOR < 3
|
||||||
|
ETH.begin(pin.eth_phy_addr, pin.eth_power, pin.eth_mdc, pin.eth_mdio, pin.eth_type, pin.eth_clk_mode);
|
||||||
|
#else
|
||||||
|
ETH.begin(pin.eth_type, pin.eth_phy_addr, pin.eth_mdc, pin.eth_mdio, pin.eth_power, pin.eth_clk_mode);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
setupMode();
|
setupMode();
|
||||||
|
|
||||||
scheduler.addTask(_loopTask);
|
scheduler.addTask(_loopTask);
|
||||||
_loopTask.enable();
|
_loopTask.enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkSettingsClass::NetworkEvent(const WiFiEvent_t event)
|
void NetworkSettingsClass::NetworkEvent(const WiFiEvent_t event, WiFiEventInfo_t info)
|
||||||
{
|
{
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case ARDUINO_EVENT_ETH_START:
|
case ARDUINO_EVENT_ETH_START:
|
||||||
@ -76,7 +90,8 @@ void NetworkSettingsClass::NetworkEvent(const WiFiEvent_t event)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||||
MessageOutput.println("WiFi disconnected");
|
// Reason codes can be found here: https://github.com/espressif/esp-idf/blob/5454d37d496a8c58542eb450467471404c606501/components/esp_wifi/include/esp_wifi_types_generic.h#L79-L141
|
||||||
|
MessageOutput.printf("WiFi disconnected: %d\r\n", info.wifi_sta_disconnected.reason);
|
||||||
if (_networkMode == network_mode::WiFi) {
|
if (_networkMode == network_mode::WiFi) {
|
||||||
MessageOutput.println("Try reconnecting");
|
MessageOutput.println("Try reconnecting");
|
||||||
WiFi.disconnect(true, false);
|
WiFi.disconnect(true, false);
|
||||||
@ -95,7 +110,7 @@ void NetworkSettingsClass::NetworkEvent(const WiFiEvent_t event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkSettingsClass::onEvent(NetworkEventCb cbEvent, const network_event event)
|
bool NetworkSettingsClass::onEvent(DtuNetworkEventCb cbEvent, const network_event event)
|
||||||
{
|
{
|
||||||
if (!cbEvent) {
|
if (!cbEvent) {
|
||||||
return pdFALSE;
|
return pdFALSE;
|
||||||
@ -109,8 +124,7 @@ bool NetworkSettingsClass::onEvent(NetworkEventCb cbEvent, const network_event e
|
|||||||
|
|
||||||
void NetworkSettingsClass::raiseEvent(const network_event event)
|
void NetworkSettingsClass::raiseEvent(const network_event event)
|
||||||
{
|
{
|
||||||
for (uint32_t i = 0; i < _cbEventList.size(); i++) {
|
for (auto& entry : _cbEventList) {
|
||||||
const NetworkEventCbList_t entry = _cbEventList[i];
|
|
||||||
if (entry.cb) {
|
if (entry.cb) {
|
||||||
if (entry.event == event || entry.event == network_event::NETWORK_EVENT_MAX) {
|
if (entry.event == event || entry.event == network_event::NETWORK_EVENT_MAX) {
|
||||||
entry.cb(event);
|
entry.cb(event);
|
||||||
@ -167,11 +181,6 @@ void NetworkSettingsClass::setupMode()
|
|||||||
WiFi.mode(WIFI_MODE_NULL);
|
WiFi.mode(WIFI_MODE_NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PinMapping.isValidEthConfig()) {
|
|
||||||
PinMapping_t& pin = PinMapping.get();
|
|
||||||
ETH.begin(pin.eth_phy_addr, pin.eth_power, pin.eth_mdc, pin.eth_mdio, pin.eth_type, pin.eth_clk_mode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkSettingsClass::enableAdminMode()
|
void NetworkSettingsClass::enableAdminMode()
|
||||||
@ -399,6 +408,9 @@ String NetworkSettingsClass::macAddress() const
|
|||||||
{
|
{
|
||||||
switch (_networkMode) {
|
switch (_networkMode) {
|
||||||
case network_mode::Ethernet:
|
case network_mode::Ethernet:
|
||||||
|
if (_w5500) {
|
||||||
|
return _w5500->macAddress();
|
||||||
|
}
|
||||||
return ETH.macAddress();
|
return ETH.macAddress();
|
||||||
break;
|
break;
|
||||||
case network_mode::WiFi:
|
case network_mode::WiFi:
|
||||||
|
|||||||
@ -84,6 +84,54 @@
|
|||||||
#define CMT_SDIO -1
|
#define CMT_SDIO -1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef W5500_MOSI
|
||||||
|
#define W5500_MOSI -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef W5500_MISO
|
||||||
|
#define W5500_MISO -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef W5500_SCLK
|
||||||
|
#define W5500_SCLK -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef W5500_CS
|
||||||
|
#define W5500_CS -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef W5500_INT
|
||||||
|
#define W5500_INT -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef W5500_RST
|
||||||
|
#define W5500_RST -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ETH_PHY_ADDR
|
||||||
|
#define ETH_PHY_ADDR -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ETH_PHY_POWER
|
||||||
|
#define ETH_PHY_POWER -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ETH_PHY_MDC
|
||||||
|
#define ETH_PHY_MDC -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ETH_PHY_MDIO
|
||||||
|
#define ETH_PHY_MDIO -1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ETH_PHY_TYPE
|
||||||
|
#define ETH_PHY_TYPE ETH_PHY_LAN8720
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ETH_CLK_MODE
|
||||||
|
#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN
|
||||||
|
#endif
|
||||||
|
|
||||||
PinMappingClass PinMapping;
|
PinMappingClass PinMapping;
|
||||||
|
|
||||||
PinMappingClass::PinMappingClass()
|
PinMappingClass::PinMappingClass()
|
||||||
@ -103,6 +151,13 @@ PinMappingClass::PinMappingClass()
|
|||||||
_pinMapping.cmt_gpio3 = CMT_GPIO3;
|
_pinMapping.cmt_gpio3 = CMT_GPIO3;
|
||||||
_pinMapping.cmt_sdio = CMT_SDIO;
|
_pinMapping.cmt_sdio = CMT_SDIO;
|
||||||
|
|
||||||
|
_pinMapping.w5500_mosi = W5500_MOSI;
|
||||||
|
_pinMapping.w5500_miso = W5500_MISO;
|
||||||
|
_pinMapping.w5500_sclk = W5500_SCLK;
|
||||||
|
_pinMapping.w5500_cs = W5500_CS;
|
||||||
|
_pinMapping.w5500_int = W5500_INT;
|
||||||
|
_pinMapping.w5500_rst = W5500_RST;
|
||||||
|
|
||||||
#ifdef OPENDTU_ETHERNET
|
#ifdef OPENDTU_ETHERNET
|
||||||
_pinMapping.eth_enabled = true;
|
_pinMapping.eth_enabled = true;
|
||||||
#else
|
#else
|
||||||
@ -164,6 +219,13 @@ bool PinMappingClass::init(const String& deviceMapping)
|
|||||||
_pinMapping.cmt_gpio3 = doc[i]["cmt"]["gpio3"] | CMT_GPIO3;
|
_pinMapping.cmt_gpio3 = doc[i]["cmt"]["gpio3"] | CMT_GPIO3;
|
||||||
_pinMapping.cmt_sdio = doc[i]["cmt"]["sdio"] | CMT_SDIO;
|
_pinMapping.cmt_sdio = doc[i]["cmt"]["sdio"] | CMT_SDIO;
|
||||||
|
|
||||||
|
_pinMapping.w5500_mosi = doc[i]["w5500"]["mosi"] | W5500_MOSI;
|
||||||
|
_pinMapping.w5500_miso = doc[i]["w5500"]["miso"] | W5500_MISO;
|
||||||
|
_pinMapping.w5500_sclk = doc[i]["w5500"]["sclk"] | W5500_SCLK;
|
||||||
|
_pinMapping.w5500_cs = doc[i]["w5500"]["cs"] | W5500_CS;
|
||||||
|
_pinMapping.w5500_int = doc[i]["w5500"]["int"] | W5500_INT;
|
||||||
|
_pinMapping.w5500_rst = doc[i]["w5500"]["rst"] | W5500_RST;
|
||||||
|
|
||||||
#ifdef OPENDTU_ETHERNET
|
#ifdef OPENDTU_ETHERNET
|
||||||
_pinMapping.eth_enabled = doc[i]["eth"]["enabled"] | true;
|
_pinMapping.eth_enabled = doc[i]["eth"]["enabled"] | true;
|
||||||
#else
|
#else
|
||||||
@ -211,7 +273,19 @@ bool PinMappingClass::isValidCmt2300Config() const
|
|||||||
&& _pinMapping.cmt_sdio >= 0;
|
&& _pinMapping.cmt_sdio >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PinMappingClass::isValidW5500Config() const
|
||||||
|
{
|
||||||
|
return _pinMapping.w5500_mosi >= 0
|
||||||
|
&& _pinMapping.w5500_miso >= 0
|
||||||
|
&& _pinMapping.w5500_sclk >= 0
|
||||||
|
&& _pinMapping.w5500_cs >= 0
|
||||||
|
&& _pinMapping.w5500_int >= 0
|
||||||
|
&& _pinMapping.w5500_rst >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool PinMappingClass::isValidEthConfig() const
|
bool PinMappingClass::isValidEthConfig() const
|
||||||
{
|
{
|
||||||
return _pinMapping.eth_enabled;
|
return _pinMapping.eth_enabled
|
||||||
|
&& _pinMapping.eth_mdc >= 0
|
||||||
|
&& _pinMapping.eth_mdio >= 0;
|
||||||
}
|
}
|
||||||
|
|||||||
36
src/RestartHelper.cpp
Normal file
36
src/RestartHelper.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Thomas Basler and others
|
||||||
|
*/
|
||||||
|
#include "RestartHelper.h"
|
||||||
|
#include "Display_Graphic.h"
|
||||||
|
#include "Led_Single.h"
|
||||||
|
#include <Esp.h>
|
||||||
|
|
||||||
|
RestartHelperClass RestartHelper;
|
||||||
|
|
||||||
|
RestartHelperClass::RestartHelperClass()
|
||||||
|
: _rebootTask(1 * TASK_SECOND, TASK_FOREVER, std::bind(&RestartHelperClass::loop, this))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestartHelperClass::init(Scheduler& scheduler)
|
||||||
|
{
|
||||||
|
scheduler.addTask(_rebootTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestartHelperClass::triggerRestart()
|
||||||
|
{
|
||||||
|
_rebootTask.enable();
|
||||||
|
_rebootTask.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestartHelperClass::loop()
|
||||||
|
{
|
||||||
|
if (_rebootTask.isFirstIteration()) {
|
||||||
|
LedSingle.turnAllOff();
|
||||||
|
Display.setStatus(false);
|
||||||
|
} else {
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,10 +4,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "Display_Graphic.h"
|
|
||||||
#include "Led_Single.h"
|
|
||||||
#include "MessageOutput.h"
|
#include "MessageOutput.h"
|
||||||
#include <Esp.h>
|
#include "PinMapping.h"
|
||||||
#include <LittleFS.h>
|
#include <LittleFS.h>
|
||||||
|
|
||||||
uint32_t Utils::getChipId()
|
uint32_t Utils::getChipId()
|
||||||
@ -59,16 +57,6 @@ int Utils::getTimezoneOffset()
|
|||||||
return static_cast<int>(difftime(rawtime, gmt));
|
return static_cast<int>(difftime(rawtime, gmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::restartDtu()
|
|
||||||
{
|
|
||||||
LedSingle.turnAllOff();
|
|
||||||
Display.setStatus(false);
|
|
||||||
yield();
|
|
||||||
delay(1000);
|
|
||||||
yield();
|
|
||||||
ESP.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Utils::checkJsonAlloc(const JsonDocument& doc, const char* function, const uint16_t line)
|
bool Utils::checkJsonAlloc(const JsonDocument& doc, const char* function, const uint16_t line)
|
||||||
{
|
{
|
||||||
if (doc.overflowed()) {
|
if (doc.overflowed()) {
|
||||||
|
|||||||
111
src/W5500.cpp
Normal file
111
src/W5500.cpp
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Thomas Basler and others
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "W5500.h"
|
||||||
|
|
||||||
|
#include <SpiManager.h>
|
||||||
|
#include <driver/spi_master.h>
|
||||||
|
|
||||||
|
// Internal Arduino functions from WiFiGeneric
|
||||||
|
void tcpipInit();
|
||||||
|
void add_esp_interface_netif(esp_interface_t interface, esp_netif_t* esp_netif);
|
||||||
|
|
||||||
|
W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst)
|
||||||
|
: eth_handle(nullptr)
|
||||||
|
, eth_netif(nullptr)
|
||||||
|
{
|
||||||
|
gpio_reset_pin(static_cast<gpio_num_t>(pin_rst));
|
||||||
|
gpio_set_level(static_cast<gpio_num_t>(pin_rst), 0);
|
||||||
|
gpio_set_direction(static_cast<gpio_num_t>(pin_rst), GPIO_MODE_OUTPUT);
|
||||||
|
|
||||||
|
gpio_reset_pin(static_cast<gpio_num_t>(pin_cs));
|
||||||
|
gpio_reset_pin(static_cast<gpio_num_t>(pin_int));
|
||||||
|
|
||||||
|
esp_err_t err = gpio_install_isr_service(ARDUINO_ISR_FLAG);
|
||||||
|
if (err != ESP_ERR_INVALID_STATE) // don't raise an error when ISR service is already installed
|
||||||
|
ESP_ERROR_CHECK(err);
|
||||||
|
|
||||||
|
auto bus_config = std::make_shared<SpiBusConfig>(
|
||||||
|
static_cast<gpio_num_t>(pin_mosi),
|
||||||
|
static_cast<gpio_num_t>(pin_miso),
|
||||||
|
static_cast<gpio_num_t>(pin_sclk));
|
||||||
|
|
||||||
|
spi_device_interface_config_t device_config {
|
||||||
|
.command_bits = 16, // actually address phase
|
||||||
|
.address_bits = 8, // actually command phase
|
||||||
|
.dummy_bits = 0,
|
||||||
|
.mode = 0,
|
||||||
|
.duty_cycle_pos = 0,
|
||||||
|
.cs_ena_pretrans = 0, // only 0 supported
|
||||||
|
.cs_ena_posttrans = 0, // only 0 supported
|
||||||
|
.clock_speed_hz = 20000000, // stable with OpenDTU Fusion shield
|
||||||
|
.input_delay_ns = 0,
|
||||||
|
.spics_io_num = pin_cs,
|
||||||
|
.flags = 0,
|
||||||
|
.queue_size = 20,
|
||||||
|
.pre_cb = nullptr,
|
||||||
|
.post_cb = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
spi_device_handle_t spi = SpiManagerInst.alloc_device("", bus_config, device_config);
|
||||||
|
if (!spi)
|
||||||
|
ESP_ERROR_CHECK(ESP_FAIL);
|
||||||
|
|
||||||
|
// Reset sequence
|
||||||
|
delayMicroseconds(500);
|
||||||
|
gpio_set_level(static_cast<gpio_num_t>(pin_rst), 1);
|
||||||
|
delayMicroseconds(1000);
|
||||||
|
|
||||||
|
// Arduino function to start networking stack if not already started
|
||||||
|
tcpipInit();
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(tcpip_adapter_set_default_eth_handlers());
|
||||||
|
|
||||||
|
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi);
|
||||||
|
w5500_config.int_gpio_num = pin_int;
|
||||||
|
|
||||||
|
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||||
|
mac_config.rx_task_stack_size = 4096;
|
||||||
|
esp_eth_mac_t* mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
|
||||||
|
|
||||||
|
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
|
||||||
|
phy_config.reset_gpio_num = -1;
|
||||||
|
esp_eth_phy_t* phy = esp_eth_phy_new_w5500(&phy_config);
|
||||||
|
|
||||||
|
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
|
||||||
|
ESP_ERROR_CHECK(esp_eth_driver_install(ð_config, ð_handle));
|
||||||
|
|
||||||
|
// Configure MAC address
|
||||||
|
uint8_t mac_addr[6];
|
||||||
|
ESP_ERROR_CHECK(esp_read_mac(mac_addr, ESP_MAC_ETH));
|
||||||
|
ESP_ERROR_CHECK(esp_eth_ioctl(eth_handle, ETH_CMD_S_MAC_ADDR, mac_addr));
|
||||||
|
|
||||||
|
esp_netif_config_t netif_config = ESP_NETIF_DEFAULT_ETH();
|
||||||
|
eth_netif = esp_netif_new(&netif_config);
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)));
|
||||||
|
|
||||||
|
// Add to Arduino
|
||||||
|
add_esp_interface_netif(ESP_IF_ETH, eth_netif);
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_eth_start(eth_handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
W5500::~W5500()
|
||||||
|
{
|
||||||
|
// TODO(LennartF22): support cleanup at some point?
|
||||||
|
}
|
||||||
|
|
||||||
|
String W5500::macAddress()
|
||||||
|
{
|
||||||
|
uint8_t mac_addr[6] = {};
|
||||||
|
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);
|
||||||
|
|
||||||
|
char mac_addr_str[18];
|
||||||
|
snprintf(
|
||||||
|
mac_addr_str, sizeof(mac_addr_str), "%02X:%02X:%02X:%02X:%02X:%02X",
|
||||||
|
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
|
||||||
|
return String(mac_addr_str);
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "WebApi_config.h"
|
#include "WebApi_config.h"
|
||||||
#include "Configuration.h"
|
#include "Configuration.h"
|
||||||
|
#include "RestartHelper.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "WebApi.h"
|
#include "WebApi.h"
|
||||||
#include "WebApi_errors.h"
|
#include "WebApi_errors.h"
|
||||||
@ -61,7 +62,7 @@ void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
auto& retMsg = response->getRoot();
|
auto& retMsg = response->getRoot();
|
||||||
|
|
||||||
if (!(root.containsKey("delete"))) {
|
if (!(root["delete"].is<bool>())) {
|
||||||
retMsg["message"] = "Values are missing!";
|
retMsg["message"] = "Values are missing!";
|
||||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
@ -82,7 +83,7 @@ void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request)
|
|||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
|
|
||||||
Utils::removeAllFiles();
|
Utils::removeAllFiles();
|
||||||
Utils::restartDtu();
|
RestartHelper.triggerRestart();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiConfigClass::onConfigListGet(AsyncWebServerRequest* request)
|
void WebApiConfigClass::onConfigListGet(AsyncWebServerRequest* request)
|
||||||
@ -124,7 +125,7 @@ void WebApiConfigClass::onConfigUploadFinish(AsyncWebServerRequest* request)
|
|||||||
response->addHeader("Connection", "close");
|
response->addHeader("Connection", "close");
|
||||||
response->addHeader("Access-Control-Allow-Origin", "*");
|
response->addHeader("Access-Control-Allow-Origin", "*");
|
||||||
request->send(response);
|
request->send(response);
|
||||||
Utils::restartDtu();
|
RestartHelper.triggerRestart();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiConfigClass::onConfigUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final)
|
void WebApiConfigClass::onConfigUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final)
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
#include "Configuration.h"
|
#include "Configuration.h"
|
||||||
#include "Display_Graphic.h"
|
#include "Display_Graphic.h"
|
||||||
#include "PinMapping.h"
|
#include "PinMapping.h"
|
||||||
#include "Utils.h"
|
#include "RestartHelper.h"
|
||||||
#include "WebApi.h"
|
#include "WebApi.h"
|
||||||
#include "WebApi_errors.h"
|
#include "WebApi_errors.h"
|
||||||
#include "helper.h"
|
#include "helper.h"
|
||||||
@ -50,6 +50,14 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
|
|||||||
cmtPinObj["gpio2"] = pin.cmt_gpio2;
|
cmtPinObj["gpio2"] = pin.cmt_gpio2;
|
||||||
cmtPinObj["gpio3"] = pin.cmt_gpio3;
|
cmtPinObj["gpio3"] = pin.cmt_gpio3;
|
||||||
|
|
||||||
|
auto w5500PinObj = curPin["w5500"].to<JsonObject>();
|
||||||
|
w5500PinObj["sclk"] = pin.w5500_sclk;
|
||||||
|
w5500PinObj["mosi"] = pin.w5500_mosi;
|
||||||
|
w5500PinObj["miso"] = pin.w5500_miso;
|
||||||
|
w5500PinObj["cs"] = pin.w5500_cs;
|
||||||
|
w5500PinObj["int"] = pin.w5500_int;
|
||||||
|
w5500PinObj["rst"] = pin.w5500_rst;
|
||||||
|
|
||||||
auto ethPinObj = curPin["eth"].to<JsonObject>();
|
auto ethPinObj = curPin["eth"].to<JsonObject>();
|
||||||
ethPinObj["enabled"] = pin.eth_enabled;
|
ethPinObj["enabled"] = pin.eth_enabled;
|
||||||
ethPinObj["phy_addr"] = pin.eth_phy_addr;
|
ethPinObj["phy_addr"] = pin.eth_phy_addr;
|
||||||
@ -103,8 +111,8 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
auto& retMsg = response->getRoot();
|
auto& retMsg = response->getRoot();
|
||||||
|
|
||||||
if (!(root.containsKey("curPin")
|
if (!(root["curPin"].is<JsonObject>()
|
||||||
|| root.containsKey("display"))) {
|
|| root["display"].is<JsonObject>())) {
|
||||||
retMsg["message"] = "Values are missing!";
|
retMsg["message"] = "Values are missing!";
|
||||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
@ -149,6 +157,6 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request)
|
|||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
|
|
||||||
if (performRestart) {
|
if (performRestart) {
|
||||||
Utils::restartDtu();
|
RestartHelper.triggerRestart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,12 +90,12 @@ void WebApiDtuClass::onDtuAdminPost(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
auto& retMsg = response->getRoot();
|
auto& retMsg = response->getRoot();
|
||||||
|
|
||||||
if (!(root.containsKey("serial")
|
if (!(root["serial"].is<String>()
|
||||||
&& root.containsKey("pollinterval")
|
&& root["pollinterval"].is<uint32_t>()
|
||||||
&& root.containsKey("nrf_palevel")
|
&& root["nrf_palevel"].is<uint8_t>()
|
||||||
&& root.containsKey("cmt_palevel")
|
&& root["cmt_palevel"].is<int8_t>()
|
||||||
&& root.containsKey("cmt_frequency")
|
&& root["cmt_frequency"].is<uint32_t>()
|
||||||
&& root.containsKey("cmt_country"))) {
|
&& root["cmt_country"].is<uint8_t>())) {
|
||||||
retMsg["message"] = "Values are missing!";
|
retMsg["message"] = "Values are missing!";
|
||||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "WebApi_firmware.h"
|
#include "WebApi_firmware.h"
|
||||||
#include "Configuration.h"
|
#include "Configuration.h"
|
||||||
|
#include "RestartHelper.h"
|
||||||
#include "Update.h"
|
#include "Update.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "WebApi.h"
|
#include "WebApi.h"
|
||||||
@ -37,7 +38,7 @@ void WebApiFirmwareClass::onFirmwareUpdateFinish(AsyncWebServerRequest* request)
|
|||||||
response->addHeader("Connection", "close");
|
response->addHeader("Connection", "close");
|
||||||
response->addHeader("Access-Control-Allow-Origin", "*");
|
response->addHeader("Access-Control-Allow-Origin", "*");
|
||||||
request->send(response);
|
request->send(response);
|
||||||
Utils::restartDtu();
|
RestartHelper.triggerRestart();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiFirmwareClass::onFirmwareUpdateUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final)
|
void WebApiFirmwareClass::onFirmwareUpdateUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final)
|
||||||
|
|||||||
@ -21,6 +21,7 @@ void WebApiInverterClass::init(AsyncWebServer& server, Scheduler& scheduler)
|
|||||||
server.on("/api/inverter/edit", HTTP_POST, std::bind(&WebApiInverterClass::onInverterEdit, this, _1));
|
server.on("/api/inverter/edit", HTTP_POST, std::bind(&WebApiInverterClass::onInverterEdit, this, _1));
|
||||||
server.on("/api/inverter/del", HTTP_POST, std::bind(&WebApiInverterClass::onInverterDelete, this, _1));
|
server.on("/api/inverter/del", HTTP_POST, std::bind(&WebApiInverterClass::onInverterDelete, this, _1));
|
||||||
server.on("/api/inverter/order", HTTP_POST, std::bind(&WebApiInverterClass::onInverterOrder, this, _1));
|
server.on("/api/inverter/order", HTTP_POST, std::bind(&WebApiInverterClass::onInverterOrder, this, _1));
|
||||||
|
server.on("/api/inverter/stats_reset", HTTP_GET, std::bind(&WebApiInverterClass::onInverterStatReset, this, _1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
|
void WebApiInverterClass::onInverterList(AsyncWebServerRequest* request)
|
||||||
@ -95,8 +96,8 @@ void WebApiInverterClass::onInverterAdd(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
auto& retMsg = response->getRoot();
|
auto& retMsg = response->getRoot();
|
||||||
|
|
||||||
if (!(root.containsKey("serial")
|
if (!(root["serial"].is<String>()
|
||||||
&& root.containsKey("name"))) {
|
&& root["name"].is<String>())) {
|
||||||
retMsg["message"] = "Values are missing!";
|
retMsg["message"] = "Values are missing!";
|
||||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
@ -165,7 +166,10 @@ void WebApiInverterClass::onInverterEdit(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
auto& retMsg = response->getRoot();
|
auto& retMsg = response->getRoot();
|
||||||
|
|
||||||
if (!(root.containsKey("id") && root.containsKey("serial") && root.containsKey("name") && root.containsKey("channel"))) {
|
if (!(root["id"].is<uint8_t>()
|
||||||
|
&& root["serial"].is<String>()
|
||||||
|
&& root["name"].is<String>()
|
||||||
|
&& root["channel"].is<JsonArray>())) {
|
||||||
retMsg["message"] = "Values are missing!";
|
retMsg["message"] = "Values are missing!";
|
||||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
@ -281,7 +285,7 @@ void WebApiInverterClass::onInverterDelete(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
auto& retMsg = response->getRoot();
|
auto& retMsg = response->getRoot();
|
||||||
|
|
||||||
if (!(root.containsKey("id"))) {
|
if (!(root["id"].is<uint8_t>())) {
|
||||||
retMsg["message"] = "Values are missing!";
|
retMsg["message"] = "Values are missing!";
|
||||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
@ -323,7 +327,7 @@ void WebApiInverterClass::onInverterOrder(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
auto& retMsg = response->getRoot();
|
auto& retMsg = response->getRoot();
|
||||||
|
|
||||||
if (!(root.containsKey("order"))) {
|
if (!(root["order"].is<JsonArray>())) {
|
||||||
retMsg["message"] = "Values are missing!";
|
retMsg["message"] = "Values are missing!";
|
||||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
@ -346,3 +350,24 @@ void WebApiInverterClass::onInverterOrder(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebApiInverterClass::onInverterStatReset(AsyncWebServerRequest* request)
|
||||||
|
{
|
||||||
|
if (!WebApi.checkCredentials(request)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncJsonResponse* response = new AsyncJsonResponse();
|
||||||
|
auto retMsg = response->getRoot();
|
||||||
|
auto serial = WebApi.parseSerialFromRequest(request);
|
||||||
|
auto inv = Hoymiles.getInverterBySerial(serial);
|
||||||
|
|
||||||
|
if (inv != nullptr) {
|
||||||
|
inv->resetRadioStats();
|
||||||
|
retMsg["type"] = "success";
|
||||||
|
retMsg["message"] = "Stats resetted";
|
||||||
|
retMsg["code"] = WebApiError::InverterStatsResetted;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
|
}
|
||||||
|
|||||||
@ -64,9 +64,9 @@ void WebApiLimitClass::onLimitPost(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
auto& retMsg = response->getRoot();
|
auto& retMsg = response->getRoot();
|
||||||
|
|
||||||
if (!(root.containsKey("serial")
|
if (!(root["serial"].is<String>()
|
||||||
&& root.containsKey("limit_value")
|
&& root["limit_value"].is<float>()
|
||||||
&& root.containsKey("limit_type"))) {
|
&& root["limit_type"].is<uint16_t>())) {
|
||||||
retMsg["message"] = "Values are missing!";
|
retMsg["message"] = "Values are missing!";
|
||||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "WebApi_maintenance.h"
|
#include "WebApi_maintenance.h"
|
||||||
#include "Utils.h"
|
#include "RestartHelper.h"
|
||||||
#include "WebApi.h"
|
#include "WebApi.h"
|
||||||
#include "WebApi_errors.h"
|
#include "WebApi_errors.h"
|
||||||
#include <AsyncJson.h>
|
#include <AsyncJson.h>
|
||||||
@ -30,7 +30,7 @@ void WebApiMaintenanceClass::onRebootPost(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
auto& retMsg = response->getRoot();
|
auto& retMsg = response->getRoot();
|
||||||
|
|
||||||
if (!(root.containsKey("reboot"))) {
|
if (!(root["reboot"].is<bool>())) {
|
||||||
retMsg["message"] = "Values are missing!";
|
retMsg["message"] = "Values are missing!";
|
||||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
@ -43,7 +43,7 @@ void WebApiMaintenanceClass::onRebootPost(AsyncWebServerRequest* request)
|
|||||||
retMsg["code"] = WebApiError::MaintenanceRebootTriggered;
|
retMsg["code"] = WebApiError::MaintenanceRebootTriggered;
|
||||||
|
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
Utils::restartDtu();
|
RestartHelper.triggerRestart();
|
||||||
} else {
|
} else {
|
||||||
retMsg["message"] = "Reboot cancled!";
|
retMsg["message"] = "Reboot cancled!";
|
||||||
retMsg["code"] = WebApiError::MaintenanceRebootCancled;
|
retMsg["code"] = WebApiError::MaintenanceRebootCancled;
|
||||||
|
|||||||
@ -107,29 +107,29 @@ void WebApiMqttClass::onMqttAdminPost(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
auto& retMsg = response->getRoot();
|
auto& retMsg = response->getRoot();
|
||||||
|
|
||||||
if (!(root.containsKey("mqtt_enabled")
|
if (!(root["mqtt_enabled"].is<bool>()
|
||||||
&& root.containsKey("mqtt_hostname")
|
&& root["mqtt_hostname"].is<String>()
|
||||||
&& root.containsKey("mqtt_port")
|
&& root["mqtt_port"].is<uint>()
|
||||||
&& root.containsKey("mqtt_clientid")
|
&& root["mqtt_clientid"].is<String>()
|
||||||
&& root.containsKey("mqtt_username")
|
&& root["mqtt_username"].is<String>()
|
||||||
&& root.containsKey("mqtt_password")
|
&& root["mqtt_password"].is<String>()
|
||||||
&& root.containsKey("mqtt_topic")
|
&& root["mqtt_topic"].is<String>()
|
||||||
&& root.containsKey("mqtt_retain")
|
&& root["mqtt_retain"].is<bool>()
|
||||||
&& root.containsKey("mqtt_tls")
|
&& root["mqtt_tls"].is<bool>()
|
||||||
&& root.containsKey("mqtt_tls_cert_login")
|
&& root["mqtt_tls_cert_login"].is<bool>()
|
||||||
&& root.containsKey("mqtt_client_cert")
|
&& root["mqtt_client_cert"].is<String>()
|
||||||
&& root.containsKey("mqtt_client_key")
|
&& root["mqtt_client_key"].is<String>()
|
||||||
&& root.containsKey("mqtt_lwt_topic")
|
&& root["mqtt_lwt_topic"].is<String>()
|
||||||
&& root.containsKey("mqtt_lwt_online")
|
&& root["mqtt_lwt_online"].is<String>()
|
||||||
&& root.containsKey("mqtt_lwt_offline")
|
&& root["mqtt_lwt_offline"].is<String>()
|
||||||
&& root.containsKey("mqtt_lwt_qos")
|
&& root["mqtt_lwt_qos"].is<uint8_t>()
|
||||||
&& root.containsKey("mqtt_publish_interval")
|
&& root["mqtt_publish_interval"].is<uint32_t>()
|
||||||
&& root.containsKey("mqtt_clean_session")
|
&& root["mqtt_clean_session"].is<bool>()
|
||||||
&& root.containsKey("mqtt_hass_enabled")
|
&& root["mqtt_hass_enabled"].is<bool>()
|
||||||
&& root.containsKey("mqtt_hass_expire")
|
&& root["mqtt_hass_expire"].is<bool>()
|
||||||
&& root.containsKey("mqtt_hass_retain")
|
&& root["mqtt_hass_retain"].is<bool>()
|
||||||
&& root.containsKey("mqtt_hass_topic")
|
&& root["mqtt_hass_topic"].is<String>()
|
||||||
&& root.containsKey("mqtt_hass_individualpanels"))) {
|
&& root["mqtt_hass_individualpanels"].is<bool>())) {
|
||||||
retMsg["message"] = "Values are missing!";
|
retMsg["message"] = "Values are missing!";
|
||||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
|
|||||||
@ -88,16 +88,16 @@ void WebApiNetworkClass::onNetworkAdminPost(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
auto& retMsg = response->getRoot();
|
auto& retMsg = response->getRoot();
|
||||||
|
|
||||||
if (!(root.containsKey("ssid")
|
if (!(root["ssid"].is<String>()
|
||||||
&& root.containsKey("password")
|
&& root["password"].is<String>()
|
||||||
&& root.containsKey("hostname")
|
&& root["hostname"].is<String>()
|
||||||
&& root.containsKey("dhcp")
|
&& root["dhcp"].is<bool>()
|
||||||
&& root.containsKey("ipaddress")
|
&& root["ipaddress"].is<String>()
|
||||||
&& root.containsKey("netmask")
|
&& root["netmask"].is<String>()
|
||||||
&& root.containsKey("gateway")
|
&& root["gateway"].is<String>()
|
||||||
&& root.containsKey("dns1")
|
&& root["dns1"].is<String>()
|
||||||
&& root.containsKey("dns2")
|
&& root["dns2"].is<String>()
|
||||||
&& root.containsKey("aptimeout"))) {
|
&& root["aptimeout"].is<uint>())) {
|
||||||
retMsg["message"] = "Values are missing!";
|
retMsg["message"] = "Values are missing!";
|
||||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
|
|||||||
@ -100,11 +100,11 @@ void WebApiNtpClass::onNtpAdminPost(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
auto& retMsg = response->getRoot();
|
auto& retMsg = response->getRoot();
|
||||||
|
|
||||||
if (!(root.containsKey("ntp_server")
|
if (!(root["ntp_server"].is<String>()
|
||||||
&& root.containsKey("ntp_timezone")
|
&& root["ntp_timezone"].is<String>()
|
||||||
&& root.containsKey("longitude")
|
&& root["longitude"].is<double>()
|
||||||
&& root.containsKey("latitude")
|
&& root["latitude"].is<double>()
|
||||||
&& root.containsKey("sunsettype"))) {
|
&& root["sunsettype"].is<uint8_t>())) {
|
||||||
retMsg["message"] = "Values are missing!";
|
retMsg["message"] = "Values are missing!";
|
||||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
@ -193,12 +193,12 @@ void WebApiNtpClass::onNtpTimePost(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
auto& retMsg = response->getRoot();
|
auto& retMsg = response->getRoot();
|
||||||
|
|
||||||
if (!(root.containsKey("year")
|
if (!(root["year"].is<uint>()
|
||||||
&& root.containsKey("month")
|
&& root["month"].is<uint>()
|
||||||
&& root.containsKey("day")
|
&& root["day"].is<uint>()
|
||||||
&& root.containsKey("hour")
|
&& root["hour"].is<uint>()
|
||||||
&& root.containsKey("minute")
|
&& root["minute"].is<uint>()
|
||||||
&& root.containsKey("second"))) {
|
&& root["second"].is<uint>())) {
|
||||||
retMsg["message"] = "Values are missing!";
|
retMsg["message"] = "Values are missing!";
|
||||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
|
|||||||
@ -57,9 +57,9 @@ void WebApiPowerClass::onPowerPost(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
auto& retMsg = response->getRoot();
|
auto& retMsg = response->getRoot();
|
||||||
|
|
||||||
if (!(root.containsKey("serial")
|
if (!(root["serial"].is<String>()
|
||||||
&& (root.containsKey("power")
|
&& (root["power"].is<bool>()
|
||||||
|| root.containsKey("restart")))) {
|
|| root["restart"].is<bool>()))) {
|
||||||
retMsg["message"] = "Values are missing!";
|
retMsg["message"] = "Values are missing!";
|
||||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
@ -84,8 +84,8 @@ void WebApiPowerClass::onPowerPost(AsyncWebServerRequest* request)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.containsKey("power")) {
|
if (root["power"].is<bool>()) {
|
||||||
uint16_t power = root["power"].as<bool>();
|
bool power = root["power"].as<bool>();
|
||||||
inv->sendPowerControlRequest(power);
|
inv->sendPowerControlRequest(power);
|
||||||
} else {
|
} else {
|
||||||
if (root["restart"].as<bool>()) {
|
if (root["restart"].as<bool>()) {
|
||||||
|
|||||||
@ -48,8 +48,8 @@ void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request)
|
|||||||
|
|
||||||
auto& retMsg = response->getRoot();
|
auto& retMsg = response->getRoot();
|
||||||
|
|
||||||
if (!root.containsKey("password")
|
if (!root["password"].is<String>()
|
||||||
&& root.containsKey("allow_readonly")) {
|
&& root["allow_readonly"].is<bool>()) {
|
||||||
retMsg["message"] = "Values are missing!";
|
retMsg["message"] = "Values are missing!";
|
||||||
retMsg["code"] = WebApiError::GenericValueMissing;
|
retMsg["code"] = WebApiError::GenericValueMissing;
|
||||||
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__);
|
||||||
|
|||||||
@ -134,6 +134,12 @@ void WebApiWsLiveClass::generateInverterCommonJsonResponse(JsonObject& root, std
|
|||||||
} else {
|
} else {
|
||||||
root["limit_absolute"] = -1;
|
root["limit_absolute"] = -1;
|
||||||
}
|
}
|
||||||
|
root["radio_stats"]["tx_request"] = inv->RadioStats.TxRequestData;
|
||||||
|
root["radio_stats"]["tx_re_request"] = inv->RadioStats.TxReRequestFragment;
|
||||||
|
root["radio_stats"]["rx_success"] = inv->RadioStats.RxSuccess;
|
||||||
|
root["radio_stats"]["rx_fail_nothing"] = inv->RadioStats.RxFailNoAnswer;
|
||||||
|
root["radio_stats"]["rx_fail_partial"] = inv->RadioStats.RxFailPartialAnswer;
|
||||||
|
root["radio_stats"]["rx_fail_corrupt"] = inv->RadioStats.RxFailCorruptData;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApiWsLiveClass::generateInverterChannelJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> inv)
|
void WebApiWsLiveClass::generateInverterChannelJsonResponse(JsonObject& root, std::shared_ptr<InverterAbstract> inv)
|
||||||
|
|||||||
11
src/main.cpp
11
src/main.cpp
@ -16,6 +16,7 @@
|
|||||||
#include "NetworkSettings.h"
|
#include "NetworkSettings.h"
|
||||||
#include "NtpSettings.h"
|
#include "NtpSettings.h"
|
||||||
#include "PinMapping.h"
|
#include "PinMapping.h"
|
||||||
|
#include "RestartHelper.h"
|
||||||
#include "Scheduler.h"
|
#include "Scheduler.h"
|
||||||
#include "SunPosition.h"
|
#include "SunPosition.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
@ -25,12 +26,21 @@
|
|||||||
#include <LittleFS.h>
|
#include <LittleFS.h>
|
||||||
#include <TaskScheduler.h>
|
#include <TaskScheduler.h>
|
||||||
#include <esp_heap_caps.h>
|
#include <esp_heap_caps.h>
|
||||||
|
#include <SpiManager.h>
|
||||||
|
|
||||||
|
#include <driver/uart.h>
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
// Move all dynamic allocations >512byte to psram (if available)
|
// Move all dynamic allocations >512byte to psram (if available)
|
||||||
heap_caps_malloc_extmem_enable(512);
|
heap_caps_malloc_extmem_enable(512);
|
||||||
|
|
||||||
|
// Initialize SpiManager
|
||||||
|
SpiManagerInst.register_bus(SPI2_HOST);
|
||||||
|
#if SOC_SPI_PERIPH_NUM > 2
|
||||||
|
SpiManagerInst.register_bus(SPI3_HOST);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Initialize serial output
|
// Initialize serial output
|
||||||
Serial.begin(SERIAL_BAUDRATE);
|
Serial.begin(SERIAL_BAUDRATE);
|
||||||
#if ARDUINO_USB_CDC_ON_BOOT
|
#if ARDUINO_USB_CDC_ON_BOOT
|
||||||
@ -154,6 +164,7 @@ void setup()
|
|||||||
InverterSettings.init(scheduler);
|
InverterSettings.init(scheduler);
|
||||||
|
|
||||||
Datastore.init(scheduler);
|
Datastore.init(scheduler);
|
||||||
|
RestartHelper.init(scheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
|
|||||||
@ -17,34 +17,35 @@
|
|||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
"bootstrap-icons-vue": "^1.11.3",
|
"bootstrap-icons-vue": "^1.11.3",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"sortablejs": "^1.15.2",
|
"sortablejs": "^1.15.3",
|
||||||
"spark-md5": "^3.0.2",
|
"spark-md5": "^3.0.2",
|
||||||
"vue": "^3.4.35",
|
"vue": "^3.5.10",
|
||||||
"vue-i18n": "^9.13.1",
|
"vue-i18n": "9.13.1",
|
||||||
"vue-router": "^4.4.2"
|
"vue-router": "^4.4.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
||||||
"@tsconfig/node18": "^18.2.4",
|
"@tsconfig/node22": "^22.0.0",
|
||||||
"@types/bootstrap": "^5.2.10",
|
"@types/bootstrap": "^5.2.10",
|
||||||
"@types/node": "^22.1.0",
|
"@types/node": "^22.7.4",
|
||||||
"@types/pulltorefreshjs": "^0.1.7",
|
"@types/pulltorefreshjs": "^0.1.7",
|
||||||
"@types/sortablejs": "^1.15.8",
|
"@types/sortablejs": "^1.15.8",
|
||||||
"@types/spark-md5": "^3.0.4",
|
"@types/spark-md5": "^3.0.4",
|
||||||
"@vitejs/plugin-vue": "^5.1.2",
|
"@vitejs/plugin-vue": "^5.1.4",
|
||||||
"@vue/eslint-config-typescript": "^13.0.0",
|
"@vue/eslint-config-typescript": "^13.0.0",
|
||||||
"@vue/tsconfig": "^0.5.1",
|
"@vue/tsconfig": "^0.5.1",
|
||||||
"eslint": "^9.8.0",
|
"eslint": "^9.11.1",
|
||||||
"eslint-plugin-vue": "^9.27.0",
|
"eslint-plugin-vue": "^9.28.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"pulltorefreshjs": "^0.1.22",
|
"pulltorefreshjs": "^0.1.22",
|
||||||
"sass": "^1.77.6",
|
"sass": "^1.77.6",
|
||||||
"terser": "^5.31.3",
|
"terser": "^5.34.0",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.6.2",
|
||||||
"vite": "^5.3.5",
|
"vite": "^5.4.8",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-css-injected-by-js": "^3.5.1",
|
"vite-plugin-css-injected-by-js": "^3.5.2",
|
||||||
"vue-tsc": "^2.0.29"
|
"vue-tsc": "^2.1.6"
|
||||||
}
|
},
|
||||||
|
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<th scope="col">{{ $t('eventlog.Start') }}</th>
|
<tr>
|
||||||
<th scope="col">{{ $t('eventlog.Stop') }}</th>
|
<th scope="col">{{ $t('eventlog.Start') }}</th>
|
||||||
<th scope="col">{{ $t('eventlog.Id') }}</th>
|
<th scope="col">{{ $t('eventlog.Stop') }}</th>
|
||||||
<th scope="col">{{ $t('eventlog.Message') }}</th>
|
<th scope="col">{{ $t('eventlog.Id') }}</th>
|
||||||
|
<th scope="col">{{ $t('eventlog.Message') }}</th>
|
||||||
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<template v-for="event in eventLogList.count" :key="event">
|
<template v-for="event in eventLogList.count" :key="event">
|
||||||
|
|||||||
@ -141,7 +141,16 @@
|
|||||||
"Unknown": "Unbekannt",
|
"Unknown": "Unbekannt",
|
||||||
"ShowGridProfile": "Zeige Grid Profil",
|
"ShowGridProfile": "Zeige Grid Profil",
|
||||||
"GridProfile": "Grid Profil",
|
"GridProfile": "Grid Profil",
|
||||||
"LoadingInverter": "Warte auf Daten... (kann bis zu 10 Sekunden dauern)"
|
"LoadingInverter": "Warte auf Daten... (kann bis zu 10 Sekunden dauern)",
|
||||||
|
"RadioStats": "Funkstatistik",
|
||||||
|
"TxRequest": "Gesendete Anfragen",
|
||||||
|
"RxSuccess": "Empfang Erfolgreich",
|
||||||
|
"RxFailNothing": "Empfang Fehler: Nichts empfangen",
|
||||||
|
"RxFailPartial": "Empfang Fehler: Teilweise empfangen",
|
||||||
|
"RxFailCorrupt": "Empfang Fehler: Beschädigt empfangen",
|
||||||
|
"TxReRequest": "Gesendete Fragment Wiederanforderungen",
|
||||||
|
"StatsReset": "Statistiken zurücksetzen",
|
||||||
|
"StatsResetting": "Zurücksetzen..."
|
||||||
},
|
},
|
||||||
"eventlog": {
|
"eventlog": {
|
||||||
"Start": "Beginn",
|
"Start": "Beginn",
|
||||||
|
|||||||
@ -141,7 +141,16 @@
|
|||||||
"Unknown": "Unknown",
|
"Unknown": "Unknown",
|
||||||
"ShowGridProfile": "Show Grid Profile",
|
"ShowGridProfile": "Show Grid Profile",
|
||||||
"GridProfile": "Grid Profile",
|
"GridProfile": "Grid Profile",
|
||||||
"LoadingInverter": "Waiting for data... (can take up to 10 seconds)"
|
"LoadingInverter": "Waiting for data... (can take up to 10 seconds)",
|
||||||
|
"RadioStats": "Radio Statistics",
|
||||||
|
"TxRequest": "TX Request Count",
|
||||||
|
"RxSuccess": "RX Success",
|
||||||
|
"RxFailNothing": "RX Fail: Receive Nothing",
|
||||||
|
"RxFailPartial": "RX Fail: Receive Partial",
|
||||||
|
"RxFailCorrupt": "RX Fail: Receive Corrupt",
|
||||||
|
"TxReRequest": "TX Re-Request Fragment",
|
||||||
|
"StatsReset": "Reset Statistics",
|
||||||
|
"StatsResetting": "Resetting..."
|
||||||
},
|
},
|
||||||
"eventlog": {
|
"eventlog": {
|
||||||
"Start": "Start",
|
"Start": "Start",
|
||||||
|
|||||||
@ -141,7 +141,16 @@
|
|||||||
"Unknown": "Inconnu",
|
"Unknown": "Inconnu",
|
||||||
"ShowGridProfile": "Show Grid Profile",
|
"ShowGridProfile": "Show Grid Profile",
|
||||||
"GridProfile": "Grid Profile",
|
"GridProfile": "Grid Profile",
|
||||||
"LoadingInverter": "Waiting for data... (can take up to 10 seconds)"
|
"LoadingInverter": "Waiting for data... (can take up to 10 seconds)",
|
||||||
|
"RadioStats": "Radio Statistics",
|
||||||
|
"TxRequest": "TX Request Count",
|
||||||
|
"RxSuccess": "RX Success",
|
||||||
|
"RxFailNothing": "RX Fail: Receive Nothing",
|
||||||
|
"RxFailPartial": "RX Fail: Receive Partial",
|
||||||
|
"RxFailCorrupt": "RX Fail: Receive Corrupt",
|
||||||
|
"TxReRequest": "TX Re-Request Fragment",
|
||||||
|
"StatsReset": "Reset Statistics",
|
||||||
|
"StatsResetting": "Resetting..."
|
||||||
},
|
},
|
||||||
"eventlog": {
|
"eventlog": {
|
||||||
"Start": "Départ",
|
"Start": "Départ",
|
||||||
|
|||||||
@ -21,6 +21,15 @@ export interface InverterStatistics {
|
|||||||
Irradiation?: ValueObject;
|
Irradiation?: ValueObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RadioStatistics {
|
||||||
|
tx_request: number;
|
||||||
|
tx_re_request: number;
|
||||||
|
rx_success: number;
|
||||||
|
rx_fail_nothing: number;
|
||||||
|
rx_fail_partial: number;
|
||||||
|
rx_fail_corrupt: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Inverter {
|
export interface Inverter {
|
||||||
serial: string;
|
serial: string;
|
||||||
name: string;
|
name: string;
|
||||||
@ -35,6 +44,7 @@ export interface Inverter {
|
|||||||
AC: InverterStatistics[];
|
AC: InverterStatistics[];
|
||||||
DC: InverterStatistics[];
|
DC: InverterStatistics[];
|
||||||
INV: InverterStatistics[];
|
INV: InverterStatistics[];
|
||||||
|
radio_stats: RadioStatistics;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Total {
|
export interface Total {
|
||||||
|
|||||||
@ -202,7 +202,7 @@
|
|||||||
min="0"
|
min="0"
|
||||||
max="100"
|
max="100"
|
||||||
id="inputDisplayContrast"
|
id="inputDisplayContrast"
|
||||||
v-model="deviceConfigList.display.contrast"
|
v-model.number="deviceConfigList.display.contrast"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -239,7 +239,7 @@
|
|||||||
min="0"
|
min="0"
|
||||||
max="100"
|
max="100"
|
||||||
:id="getLedIdFromNumber(index)"
|
:id="getLedIdFromNumber(index)"
|
||||||
v-model="ledSetting.brightness"
|
v-model.number="ledSetting.brightness"
|
||||||
@change="syncSliders"
|
@change="syncSliders"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -48,7 +48,7 @@
|
|||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
class="form-control form-range"
|
class="form-control form-range"
|
||||||
v-model="dtuConfigList.cmt_palevel"
|
v-model.number="dtuConfigList.cmt_palevel"
|
||||||
min="-10"
|
min="-10"
|
||||||
max="20"
|
max="20"
|
||||||
id="inputCmtPaLevel"
|
id="inputCmtPaLevel"
|
||||||
@ -89,7 +89,7 @@
|
|||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
class="form-control form-range"
|
class="form-control form-range"
|
||||||
v-model="dtuConfigList.cmt_frequency"
|
v-model.number="dtuConfigList.cmt_frequency"
|
||||||
:min="cmtMinFrequency"
|
:min="cmtMinFrequency"
|
||||||
:max="cmtMaxFrequency"
|
:max="cmtMaxFrequency"
|
||||||
:step="dtuConfigList.cmt_chan_width"
|
:step="dtuConfigList.cmt_chan_width"
|
||||||
|
|||||||
@ -201,6 +201,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<BootstrapAlert class="m-3" :show="!inverter.hasOwnProperty('INV')">
|
<BootstrapAlert class="m-3" :show="!inverter.hasOwnProperty('INV')">
|
||||||
<div class="d-flex justify-content-center align-items-center">
|
<div class="d-flex justify-content-center align-items-center">
|
||||||
<div class="spinner-border m-1" role="status">
|
<div class="spinner-border m-1" role="status">
|
||||||
@ -209,6 +210,110 @@
|
|||||||
<span>{{ $t('home.LoadingInverter') }}</span>
|
<span>{{ $t('home.LoadingInverter') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</BootstrapAlert>
|
</BootstrapAlert>
|
||||||
|
|
||||||
|
<div class="accordion mt-5" id="accordionExample">
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h2 class="accordion-header">
|
||||||
|
<button
|
||||||
|
class="accordion-button collapsed"
|
||||||
|
type="button"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#collapseStats"
|
||||||
|
aria-expanded="true"
|
||||||
|
aria-controls="collapseStats"
|
||||||
|
>
|
||||||
|
<BIconBroadcast /> {{ $t('home.RadioStats') }}
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<div
|
||||||
|
id="collapseStats"
|
||||||
|
class="accordion-collapse collapse"
|
||||||
|
data-bs-parent="#accordionExample"
|
||||||
|
>
|
||||||
|
<div class="accordion-body">
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('home.TxRequest') }}</td>
|
||||||
|
<td>{{ $n(inverter.radio_stats.tx_request) }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('home.RxSuccess') }}</td>
|
||||||
|
<td>{{ $n(inverter.radio_stats.rx_success) }}</td>
|
||||||
|
<td>
|
||||||
|
{{
|
||||||
|
ratio(
|
||||||
|
inverter.radio_stats.rx_success,
|
||||||
|
inverter.radio_stats.tx_request
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('home.RxFailNothing') }}</td>
|
||||||
|
<td>{{ $n(inverter.radio_stats.rx_fail_nothing) }}</td>
|
||||||
|
<td>
|
||||||
|
{{
|
||||||
|
ratio(
|
||||||
|
inverter.radio_stats.rx_fail_nothing,
|
||||||
|
inverter.radio_stats.tx_request
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('home.RxFailPartial') }}</td>
|
||||||
|
<td>{{ $n(inverter.radio_stats.rx_fail_partial) }}</td>
|
||||||
|
<td>
|
||||||
|
{{
|
||||||
|
ratio(
|
||||||
|
inverter.radio_stats.rx_fail_partial,
|
||||||
|
inverter.radio_stats.tx_request
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('home.RxFailCorrupt') }}</td>
|
||||||
|
<td>{{ $n(inverter.radio_stats.rx_fail_corrupt) }}</td>
|
||||||
|
<td>
|
||||||
|
{{
|
||||||
|
ratio(
|
||||||
|
inverter.radio_stats.rx_fail_corrupt,
|
||||||
|
inverter.radio_stats.tx_request
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t('home.TxReRequest') }}</td>
|
||||||
|
<td>{{ $n(inverter.radio_stats.tx_re_request) }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button
|
||||||
|
:disabled="!isLogged || performRadioStatsReset"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-danger"
|
||||||
|
@click="onResetRadioStats(inverter.serial)"
|
||||||
|
>
|
||||||
|
<template v-if="!performRadioStatsReset">
|
||||||
|
{{ $t('home.StatsReset') }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span
|
||||||
|
class="spinner-border spinner-border-sm"
|
||||||
|
aria-hidden="true"
|
||||||
|
></span>
|
||||||
|
<span role="status"> {{ $t('home.StatsResetting') }}</span>
|
||||||
|
</template>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -390,6 +495,7 @@ import { authHeader, authUrl, handleResponse, isLoggedIn } from '@/utils/authent
|
|||||||
import * as bootstrap from 'bootstrap';
|
import * as bootstrap from 'bootstrap';
|
||||||
import {
|
import {
|
||||||
BIconArrowCounterclockwise,
|
BIconArrowCounterclockwise,
|
||||||
|
BIconBroadcast,
|
||||||
BIconCheckCircleFill,
|
BIconCheckCircleFill,
|
||||||
BIconCpu,
|
BIconCpu,
|
||||||
BIconExclamationCircleFill,
|
BIconExclamationCircleFill,
|
||||||
@ -415,6 +521,7 @@ export default defineComponent({
|
|||||||
InverterTotalInfo,
|
InverterTotalInfo,
|
||||||
ModalDialog,
|
ModalDialog,
|
||||||
BIconArrowCounterclockwise,
|
BIconArrowCounterclockwise,
|
||||||
|
BIconBroadcast,
|
||||||
BIconCheckCircleFill,
|
BIconCheckCircleFill,
|
||||||
BIconCpu,
|
BIconCpu,
|
||||||
BIconExclamationCircleFill,
|
BIconExclamationCircleFill,
|
||||||
@ -461,6 +568,7 @@ export default defineComponent({
|
|||||||
alertMessageLimit: '',
|
alertMessageLimit: '',
|
||||||
alertTypeLimit: 'info',
|
alertTypeLimit: 'info',
|
||||||
showAlertLimit: false,
|
showAlertLimit: false,
|
||||||
|
performRadioStatsReset: false,
|
||||||
|
|
||||||
powerSettingView: {} as bootstrap.Modal,
|
powerSettingView: {} as bootstrap.Modal,
|
||||||
powerSettingSerial: '',
|
powerSettingSerial: '',
|
||||||
@ -690,6 +798,14 @@ export default defineComponent({
|
|||||||
|
|
||||||
this.limitSettingView.show();
|
this.limitSettingView.show();
|
||||||
},
|
},
|
||||||
|
onResetRadioStats(serial: string) {
|
||||||
|
this.performRadioStatsReset = true;
|
||||||
|
fetch('/api/inverter/stats_reset?inv=' + serial, { headers: authHeader() })
|
||||||
|
.then((response) => handleResponse(response, this.$emitter, this.$router))
|
||||||
|
.then(() => {
|
||||||
|
this.performRadioStatsReset = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
onSetLimitSettings(setPersistent: boolean) {
|
onSetLimitSettings(setPersistent: boolean) {
|
||||||
this.targetLimitList.limit_type = (setPersistent ? 256 : 0) + this.targetLimitType;
|
this.targetLimitList.limit_type = (setPersistent ? 256 : 0) + this.targetLimitType;
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
@ -786,6 +902,12 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
return total;
|
return total;
|
||||||
},
|
},
|
||||||
|
ratio(val_small: number, val_large: number): string {
|
||||||
|
if (val_large == 0) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
return this.$n(val_small / val_large, 'percent');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -52,9 +52,7 @@ export default defineComponent({
|
|||||||
.then((data) => {
|
.then((data) => {
|
||||||
this.systemDataList = data;
|
this.systemDataList = data;
|
||||||
this.dataLoading = false;
|
this.dataLoading = false;
|
||||||
if (this.allowVersionInfo) {
|
this.getUpdateInfo();
|
||||||
this.getUpdateInfo();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getUpdateInfo() {
|
getUpdateInfo() {
|
||||||
@ -76,6 +74,10 @@ export default defineComponent({
|
|||||||
this.systemDataList.git_is_hash = true;
|
this.systemDataList.git_is_hash = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.allowVersionInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const fetchUrl =
|
const fetchUrl =
|
||||||
'https://api.github.com/repos/tbnobody/OpenDTU/compare/' + this.systemDataList.git_hash + '...HEAD';
|
'https://api.github.com/repos/tbnobody/OpenDTU/compare/' + this.systemDataList.git_hash + '...HEAD';
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"extends": [
|
"extends": [
|
||||||
"@tsconfig/node18/tsconfig.json",
|
"@tsconfig/node22/tsconfig.json",
|
||||||
"@vue/tsconfig/tsconfig.json"
|
"@vue/tsconfig/tsconfig.json"
|
||||||
],
|
],
|
||||||
"include": [
|
"include": [
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
},
|
},
|
||||||
"lib": ["ES2021", "DOM"],
|
"lib": ["ES2023", "DOM"],
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "Node",
|
||||||
|
|
||||||
/* Linting */
|
/* Linting */
|
||||||
|
|||||||
@ -45,6 +45,7 @@ export default defineConfig({
|
|||||||
outDir: '../webapp_dist',
|
outDir: '../webapp_dist',
|
||||||
emptyOutDir: true,
|
emptyOutDir: true,
|
||||||
minify: 'terser',
|
minify: 'terser',
|
||||||
|
chunkSizeWarningLimit: 1024,
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
output: {
|
output: {
|
||||||
// Only create one js file
|
// Only create one js file
|
||||||
|
|||||||
599
webapp/yarn.lock
599
webapp/yarn.lock
@ -7,6 +7,16 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
|
resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
|
||||||
integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
|
integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
|
||||||
|
|
||||||
|
"@babel/helper-string-parser@^7.24.8":
|
||||||
|
version "7.24.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz#5b3329c9a58803d5df425e5785865881a81ca48d"
|
||||||
|
integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==
|
||||||
|
|
||||||
|
"@babel/helper-validator-identifier@^7.24.7":
|
||||||
|
version "7.24.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db"
|
||||||
|
integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==
|
||||||
|
|
||||||
"@babel/parser@^7.16.4":
|
"@babel/parser@^7.16.4":
|
||||||
version "7.18.11"
|
version "7.18.11"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9"
|
||||||
@ -17,10 +27,21 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b"
|
||||||
integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==
|
integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==
|
||||||
|
|
||||||
"@babel/parser@^7.24.7":
|
"@babel/parser@^7.25.3":
|
||||||
version "7.24.7"
|
version "7.25.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.7.tgz#9a5226f92f0c5c8ead550b750f5608e766c8ce85"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.6.tgz#85660c5ef388cbbf6e3d2a694ee97a38f18afe2f"
|
||||||
integrity sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==
|
integrity sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==
|
||||||
|
dependencies:
|
||||||
|
"@babel/types" "^7.25.6"
|
||||||
|
|
||||||
|
"@babel/types@^7.25.6":
|
||||||
|
version "7.25.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.6.tgz#893942ddb858f32ae7a004ec9d3a76b3463ef8e6"
|
||||||
|
integrity sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-string-parser" "^7.24.8"
|
||||||
|
"@babel/helper-validator-identifier" "^7.24.7"
|
||||||
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
"@esbuild/aix-ppc64@0.21.5":
|
"@esbuild/aix-ppc64@0.21.5":
|
||||||
version "0.21.5"
|
version "0.21.5"
|
||||||
@ -161,15 +182,20 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.8.1.tgz#8c4bb756cc2aa7eaf13cfa5e69c83afb3260c20c"
|
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.8.1.tgz#8c4bb756cc2aa7eaf13cfa5e69c83afb3260c20c"
|
||||||
integrity sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==
|
integrity sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==
|
||||||
|
|
||||||
"@eslint/config-array@^0.17.1":
|
"@eslint/config-array@^0.18.0":
|
||||||
version "0.17.1"
|
version "0.18.0"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.17.1.tgz#d9b8b8b6b946f47388f32bedfd3adf29ca8f8910"
|
resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.18.0.tgz#37d8fe656e0d5e3dbaea7758ea56540867fd074d"
|
||||||
integrity sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==
|
integrity sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint/object-schema" "^2.1.4"
|
"@eslint/object-schema" "^2.1.4"
|
||||||
debug "^4.3.1"
|
debug "^4.3.1"
|
||||||
minimatch "^3.1.2"
|
minimatch "^3.1.2"
|
||||||
|
|
||||||
|
"@eslint/core@^0.6.0":
|
||||||
|
version "0.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.6.0.tgz#9930b5ba24c406d67a1760e94cdbac616a6eb674"
|
||||||
|
integrity sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==
|
||||||
|
|
||||||
"@eslint/eslintrc@^3.1.0":
|
"@eslint/eslintrc@^3.1.0":
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6"
|
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6"
|
||||||
@ -185,16 +211,23 @@
|
|||||||
minimatch "^3.1.2"
|
minimatch "^3.1.2"
|
||||||
strip-json-comments "^3.1.1"
|
strip-json-comments "^3.1.1"
|
||||||
|
|
||||||
"@eslint/js@9.8.0":
|
"@eslint/js@9.11.1":
|
||||||
version "9.8.0"
|
version "9.11.1"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.8.0.tgz#ae9bc14bb839713c5056f5018bcefa955556d3a4"
|
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.11.1.tgz#8bcb37436f9854b3d9a561440daf916acd940986"
|
||||||
integrity sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==
|
integrity sha512-/qu+TWz8WwPWc7/HcIJKi+c+MOm46GdVaSlTTQcaqaL53+GsoA6MxWp5PtTx48qbSP7ylM1Kn7nhvkugfJvRSA==
|
||||||
|
|
||||||
"@eslint/object-schema@^2.1.4":
|
"@eslint/object-schema@^2.1.4":
|
||||||
version "2.1.4"
|
version "2.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843"
|
resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843"
|
||||||
integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==
|
integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==
|
||||||
|
|
||||||
|
"@eslint/plugin-kit@^0.2.0":
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz#8712dccae365d24e9eeecb7b346f85e750ba343d"
|
||||||
|
integrity sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==
|
||||||
|
dependencies:
|
||||||
|
levn "^0.4.1"
|
||||||
|
|
||||||
"@humanwhocodes/module-importer@^1.0.1":
|
"@humanwhocodes/module-importer@^1.0.1":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
|
resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
|
||||||
@ -304,10 +337,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
|
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
|
||||||
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
|
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec@^1.4.15":
|
"@jridgewell/sourcemap-codec@^1.5.0":
|
||||||
version "1.4.15"
|
version "1.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
|
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
|
||||||
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
|
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
|
||||||
|
|
||||||
"@jridgewell/trace-mapping@^0.3.9":
|
"@jridgewell/trace-mapping@^0.3.9":
|
||||||
version "0.3.17"
|
version "0.3.17"
|
||||||
@ -357,75 +390,90 @@
|
|||||||
estree-walker "^2.0.2"
|
estree-walker "^2.0.2"
|
||||||
picomatch "^2.3.1"
|
picomatch "^2.3.1"
|
||||||
|
|
||||||
"@rollup/rollup-android-arm-eabi@4.13.0":
|
"@rollup/rollup-android-arm-eabi@4.21.2":
|
||||||
version "4.13.0"
|
version "4.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz#b98786c1304b4ff8db3a873180b778649b5dff2b"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.2.tgz#0412834dc423d1ff7be4cb1fc13a86a0cd262c11"
|
||||||
integrity sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==
|
integrity sha512-fSuPrt0ZO8uXeS+xP3b+yYTCBUd05MoSp2N/MFOgjhhUhMmchXlpTQrTpI8T+YAwAQuK7MafsCOxW7VrPMrJcg==
|
||||||
|
|
||||||
"@rollup/rollup-android-arm64@4.13.0":
|
"@rollup/rollup-android-arm64@4.21.2":
|
||||||
version "4.13.0"
|
version "4.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz#8833679af11172b1bf1ab7cb3bad84df4caf0c9e"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.2.tgz#baf1a014b13654f3b9e835388df9caf8c35389cb"
|
||||||
integrity sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==
|
integrity sha512-xGU5ZQmPlsjQS6tzTTGwMsnKUtu0WVbl0hYpTPauvbRAnmIvpInhJtgjj3mcuJpEiuUw4v1s4BimkdfDWlh7gA==
|
||||||
|
|
||||||
"@rollup/rollup-darwin-arm64@4.13.0":
|
"@rollup/rollup-darwin-arm64@4.21.2":
|
||||||
version "4.13.0"
|
version "4.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz#ef02d73e0a95d406e0eb4fd61a53d5d17775659b"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.2.tgz#0a2c364e775acdf1172fe3327662eec7c46e55b1"
|
||||||
integrity sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==
|
integrity sha512-99AhQ3/ZMxU7jw34Sq8brzXqWH/bMnf7ZVhvLk9QU2cOepbQSVTns6qoErJmSiAvU3InRqC2RRZ5ovh1KN0d0Q==
|
||||||
|
|
||||||
"@rollup/rollup-darwin-x64@4.13.0":
|
"@rollup/rollup-darwin-x64@4.21.2":
|
||||||
version "4.13.0"
|
version "4.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz#3ce5b9bcf92b3341a5c1c58a3e6bcce0ea9e7455"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.2.tgz#a972db75890dfab8df0da228c28993220a468c42"
|
||||||
integrity sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==
|
integrity sha512-ZbRaUvw2iN/y37x6dY50D8m2BnDbBjlnMPotDi/qITMJ4sIxNY33HArjikDyakhSv0+ybdUxhWxE6kTI4oX26w==
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm-gnueabihf@4.13.0":
|
"@rollup/rollup-linux-arm-gnueabihf@4.21.2":
|
||||||
version "4.13.0"
|
version "4.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz#3d3d2c018bdd8e037c6bfedd52acfff1c97e4be4"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.2.tgz#1609d0630ef61109dd19a278353e5176d92e30a1"
|
||||||
integrity sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==
|
integrity sha512-ztRJJMiE8nnU1YFcdbd9BcH6bGWG1z+jP+IPW2oDUAPxPjo9dverIOyXz76m6IPA6udEL12reYeLojzW2cYL7w==
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-gnu@4.13.0":
|
"@rollup/rollup-linux-arm-musleabihf@4.21.2":
|
||||||
version "4.13.0"
|
version "4.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz#5fc8cc978ff396eaa136d7bfe05b5b9138064143"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.2.tgz#3c1dca5f160aa2e79e4b20ff6395eab21804f266"
|
||||||
integrity sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==
|
integrity sha512-flOcGHDZajGKYpLV0JNc0VFH361M7rnV1ee+NTeC/BQQ1/0pllYcFmxpagltANYt8FYf9+kL6RSk80Ziwyhr7w==
|
||||||
|
|
||||||
"@rollup/rollup-linux-arm64-musl@4.13.0":
|
"@rollup/rollup-linux-arm64-gnu@4.21.2":
|
||||||
version "4.13.0"
|
version "4.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz#f2ae7d7bed416ffa26d6b948ac5772b520700eef"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.2.tgz#c2fe376e8b04eafb52a286668a8df7c761470ac7"
|
||||||
integrity sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==
|
integrity sha512-69CF19Kp3TdMopyteO/LJbWufOzqqXzkrv4L2sP8kfMaAQ6iwky7NoXTp7bD6/irKgknDKM0P9E/1l5XxVQAhw==
|
||||||
|
|
||||||
"@rollup/rollup-linux-riscv64-gnu@4.13.0":
|
"@rollup/rollup-linux-arm64-musl@4.21.2":
|
||||||
version "4.13.0"
|
version "4.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz#303d57a328ee9a50c85385936f31cf62306d30b6"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.2.tgz#e62a4235f01e0f66dbba587c087ca6db8008ec80"
|
||||||
integrity sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==
|
integrity sha512-48pD/fJkTiHAZTnZwR0VzHrao70/4MlzJrq0ZsILjLW/Ab/1XlVUStYyGt7tdyIiVSlGZbnliqmult/QGA2O2w==
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-gnu@4.13.0":
|
"@rollup/rollup-linux-powerpc64le-gnu@4.21.2":
|
||||||
version "4.13.0"
|
version "4.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz#f672f6508f090fc73f08ba40ff76c20b57424778"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.2.tgz#24b3457e75ee9ae5b1c198bd39eea53222a74e54"
|
||||||
integrity sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==
|
integrity sha512-cZdyuInj0ofc7mAQpKcPR2a2iu4YM4FQfuUzCVA2u4HI95lCwzjoPtdWjdpDKyHxI0UO82bLDoOaLfpZ/wviyQ==
|
||||||
|
|
||||||
"@rollup/rollup-linux-x64-musl@4.13.0":
|
"@rollup/rollup-linux-riscv64-gnu@4.21.2":
|
||||||
version "4.13.0"
|
version "4.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz#d2f34b1b157f3e7f13925bca3288192a66755a89"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.2.tgz#38edfba9620fe2ca8116c97e02bd9f2d606bde09"
|
||||||
integrity sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==
|
integrity sha512-RL56JMT6NwQ0lXIQmMIWr1SW28z4E4pOhRRNqwWZeXpRlykRIlEpSWdsgNWJbYBEWD84eocjSGDu/XxbYeCmwg==
|
||||||
|
|
||||||
"@rollup/rollup-win32-arm64-msvc@4.13.0":
|
"@rollup/rollup-linux-s390x-gnu@4.21.2":
|
||||||
version "4.13.0"
|
version "4.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz#8ffecc980ae4d9899eb2f9c4ae471a8d58d2da6b"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.2.tgz#a3bfb8bc5f1e802f8c76cff4a4be2e9f9ac36a18"
|
||||||
integrity sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==
|
integrity sha512-PMxkrWS9z38bCr3rWvDFVGD6sFeZJw4iQlhrup7ReGmfn7Oukrr/zweLhYX6v2/8J6Cep9IEA/SmjXjCmSbrMQ==
|
||||||
|
|
||||||
"@rollup/rollup-win32-ia32-msvc@4.13.0":
|
"@rollup/rollup-linux-x64-gnu@4.21.2":
|
||||||
version "4.13.0"
|
version "4.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz#a7505884f415662e088365b9218b2b03a88fc6f2"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.2.tgz#0dadf34be9199fcdda44b5985a086326344f30ad"
|
||||||
integrity sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==
|
integrity sha512-B90tYAUoLhU22olrafY3JQCFLnT3NglazdwkHyxNDYF/zAxJt5fJUB/yBoWFoIQ7SQj+KLe3iL4BhOMa9fzgpw==
|
||||||
|
|
||||||
"@rollup/rollup-win32-x64-msvc@4.13.0":
|
"@rollup/rollup-linux-x64-musl@4.21.2":
|
||||||
version "4.13.0"
|
version "4.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz#6abd79db7ff8d01a58865ba20a63cfd23d9e2a10"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.2.tgz#7b7deddce240400eb87f2406a445061b4fed99a8"
|
||||||
integrity sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==
|
integrity sha512-7twFizNXudESmC9oneLGIUmoHiiLppz/Xs5uJQ4ShvE6234K0VB1/aJYU3f/4g7PhssLGKBVCC37uRkkOi8wjg==
|
||||||
|
|
||||||
"@tsconfig/node18@^18.2.4":
|
"@rollup/rollup-win32-arm64-msvc@4.21.2":
|
||||||
version "18.2.4"
|
version "4.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/@tsconfig/node18/-/node18-18.2.4.tgz#094efbdd70f697d37c09f34067bf41bc4a828ae3"
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.2.tgz#a0ca0c5149c2cfb26fab32e6ba3f16996fbdb504"
|
||||||
integrity sha512-5xxU8vVs9/FNcvm3gE07fPbn9tl6tqGGWA9tSlwsUEkBxtRnTsNmwrV8gasZ9F/EobaSv9+nu8AxUKccw77JpQ==
|
integrity sha512-9rRero0E7qTeYf6+rFh3AErTNU1VCQg2mn7CQcI44vNUWM9Ze7MSRS/9RFuSsox+vstRt97+x3sOhEey024FRQ==
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-ia32-msvc@4.21.2":
|
||||||
|
version "4.21.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.2.tgz#aae2886beec3024203dbb5569db3a137bc385f8e"
|
||||||
|
integrity sha512-5rA4vjlqgrpbFVVHX3qkrCo/fZTj1q0Xxpg+Z7yIo3J2AilW7t2+n6Q8Jrx+4MrYpAnjttTYF8rr7bP46BPzRw==
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-x64-msvc@4.21.2":
|
||||||
|
version "4.21.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.2.tgz#e4291e3c1bc637083f87936c333cdbcad22af63b"
|
||||||
|
integrity sha512-6UUxd0+SKomjdzuAcp+HAmxw1FlGBnl1v2yEPSabtx4lBfdXHDVsW7+lQkgz9cNFJGY3AWR7+V8P5BqkD9L9nA==
|
||||||
|
|
||||||
|
"@tsconfig/node22@^22.0.0":
|
||||||
|
version "22.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@tsconfig/node22/-/node22-22.0.0.tgz#0bdaf702f2b7594383d24d7b2b8d557dcfdca1ed"
|
||||||
|
integrity sha512-twLQ77zevtxobBOD4ToAtVmuYrpeYUh3qh+TEp+08IWhpsrIflVHqQ1F1CiPxQGL7doCdBIOOCF+1Tm833faNg==
|
||||||
|
|
||||||
"@types/bootstrap@^5.2.10":
|
"@types/bootstrap@^5.2.10":
|
||||||
version "5.2.10"
|
version "5.2.10"
|
||||||
@ -444,17 +492,27 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2"
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2"
|
||||||
integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==
|
integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==
|
||||||
|
|
||||||
|
"@types/estree@^1.0.6":
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
|
||||||
|
integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
|
||||||
|
|
||||||
"@types/json-schema@^7.0.12":
|
"@types/json-schema@^7.0.12":
|
||||||
version "7.0.12"
|
version "7.0.12"
|
||||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
|
||||||
integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==
|
integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==
|
||||||
|
|
||||||
"@types/node@^22.1.0":
|
"@types/json-schema@^7.0.15":
|
||||||
version "22.1.0"
|
version "7.0.15"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.1.0.tgz#6d6adc648b5e03f0e83c78dc788c2b037d0ad94b"
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||||
integrity sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==
|
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
|
||||||
|
|
||||||
|
"@types/node@^22.7.4":
|
||||||
|
version "22.7.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.4.tgz#e35d6f48dca3255ce44256ddc05dee1c23353fcc"
|
||||||
|
integrity sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~6.13.0"
|
undici-types "~6.19.2"
|
||||||
|
|
||||||
"@types/pulltorefreshjs@^0.1.7":
|
"@types/pulltorefreshjs@^0.1.7":
|
||||||
version "0.1.7"
|
version "0.1.7"
|
||||||
@ -562,29 +620,29 @@
|
|||||||
"@typescript-eslint/types" "7.2.0"
|
"@typescript-eslint/types" "7.2.0"
|
||||||
eslint-visitor-keys "^3.4.1"
|
eslint-visitor-keys "^3.4.1"
|
||||||
|
|
||||||
"@vitejs/plugin-vue@^5.1.2":
|
"@vitejs/plugin-vue@^5.1.4":
|
||||||
version "5.1.2"
|
version "5.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.1.2.tgz#f11091e0130eca6c1ca8cfb85ee71ea53b255d31"
|
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz#72b8b705cfce36b00b59af196195146e356500c4"
|
||||||
integrity sha512-nY9IwH12qeiJqumTCLJLE7IiNx7HZ39cbHaysEUd+Myvbz9KAqd2yq+U01Kab1R/H1BmiyM2ShTYlNH32Fzo3A==
|
integrity sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==
|
||||||
|
|
||||||
"@volar/language-core@2.4.0-alpha.18", "@volar/language-core@~2.4.0-alpha.18":
|
"@volar/language-core@2.4.1", "@volar/language-core@~2.4.1":
|
||||||
version "2.4.0-alpha.18"
|
version "2.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/@volar/language-core/-/language-core-2.4.0-alpha.18.tgz#dafffd68ac07c26d69de16741187fd4c06bfa345"
|
resolved "https://registry.yarnpkg.com/@volar/language-core/-/language-core-2.4.1.tgz#267984b2b06908b78f1c016392fc75b75516595b"
|
||||||
integrity sha512-JAYeJvYQQROmVRtSBIczaPjP3DX4QW1fOqW1Ebs0d3Y3EwSNRglz03dSv0Dm61dzd0Yx3WgTW3hndDnTQqgmyg==
|
integrity sha512-9AKhC7Qn2mQYxj7Dz3bVxeOk7gGJladhWixUYKef/o0o7Bm4an+A3XvmcTHVqZ8stE6lBVH++g050tBtJ4TZPQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@volar/source-map" "2.4.0-alpha.18"
|
"@volar/source-map" "2.4.1"
|
||||||
|
|
||||||
"@volar/source-map@2.4.0-alpha.18":
|
"@volar/source-map@2.4.1":
|
||||||
version "2.4.0-alpha.18"
|
version "2.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/@volar/source-map/-/source-map-2.4.0-alpha.18.tgz#a2413932ff6b1821ae8efcbd9249d4da3f99f223"
|
resolved "https://registry.yarnpkg.com/@volar/source-map/-/source-map-2.4.1.tgz#6a6d02b9dac66a5dd99378dcdae63107a0b45fce"
|
||||||
integrity sha512-MTeCV9MUwwsH0sNFiZwKtFrrVZUK6p8ioZs3xFzHc2cvDXHWlYN3bChdQtwKX+FY2HG6H3CfAu1pKijolzIQ8g==
|
integrity sha512-Xq6ep3OZg9xUqN90jEgB9ztX5SsTz1yiV8wiQbcYNjWkek+Ie3dc8l7AVt3EhDm9mSIR58oWczHkzM2H6HIsmQ==
|
||||||
|
|
||||||
"@volar/typescript@~2.4.0-alpha.18":
|
"@volar/typescript@~2.4.1":
|
||||||
version "2.4.0-alpha.18"
|
version "2.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/@volar/typescript/-/typescript-2.4.0-alpha.18.tgz#806aca9ce1bd7c48dc5fcd0fcf7f33bdd04e5b35"
|
resolved "https://registry.yarnpkg.com/@volar/typescript/-/typescript-2.4.1.tgz#6285f29b36c58769ccc14153f329d11e89ee13bc"
|
||||||
integrity sha512-sXh5Y8sqGUkgxpMWUGvRXggxYHAVxg0Pa1C42lQZuPDrW6vHJPR0VCK8Sr7WJsAW530HuNQT/ZIskmXtxjybMQ==
|
integrity sha512-UoRzC0PXcwajFQTu8XxKSYNsWNBtVja6Y9gC8eLv7kYm+UEKJCcZ8g7dialsOYA0HKs3Vpg57MeCsawFLC6m9Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@volar/language-core" "2.4.0-alpha.18"
|
"@volar/language-core" "2.4.1"
|
||||||
path-browserify "^1.0.1"
|
path-browserify "^1.0.1"
|
||||||
vscode-uri "^3.0.8"
|
vscode-uri "^3.0.8"
|
||||||
|
|
||||||
@ -609,13 +667,13 @@
|
|||||||
estree-walker "^2.0.2"
|
estree-walker "^2.0.2"
|
||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
"@vue/compiler-core@3.4.35":
|
"@vue/compiler-core@3.5.10":
|
||||||
version "3.4.35"
|
version "3.5.10"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.35.tgz#421922a75ecabf1aabc6b7a2ce98b5acb2fc2d65"
|
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.10.tgz#dc382e4173c5ad6d309887f5cb02983dfd88cfee"
|
||||||
integrity sha512-gKp0zGoLnMYtw4uS/SJRRO7rsVggLjvot3mcctlMXunYNsX+aRJDqqw/lV5/gHK91nvaAAlWFgdVl020AW1Prg==
|
integrity sha512-iXWlk+Cg/ag7gLvY0SfVucU8Kh2CjysYZjhhP70w9qI4MvSox4frrP+vDGvtQuzIcgD8+sxM6lZvCtdxGunTAA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/parser" "^7.24.7"
|
"@babel/parser" "^7.25.3"
|
||||||
"@vue/shared" "3.4.35"
|
"@vue/shared" "3.5.10"
|
||||||
entities "^4.5.0"
|
entities "^4.5.0"
|
||||||
estree-walker "^2.0.2"
|
estree-walker "^2.0.2"
|
||||||
source-map-js "^1.2.0"
|
source-map-js "^1.2.0"
|
||||||
@ -628,13 +686,13 @@
|
|||||||
"@vue/compiler-core" "3.2.47"
|
"@vue/compiler-core" "3.2.47"
|
||||||
"@vue/shared" "3.2.47"
|
"@vue/shared" "3.2.47"
|
||||||
|
|
||||||
"@vue/compiler-dom@3.4.35":
|
"@vue/compiler-dom@3.5.10":
|
||||||
version "3.4.35"
|
version "3.5.10"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.35.tgz#cd0881f1b4ed655cd96367bce4845f87023a5a2d"
|
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.10.tgz#233c660289ce289a48e8fe759b07b95f607cd98e"
|
||||||
integrity sha512-pWIZRL76/oE/VMhdv/ovZfmuooEni6JPG1BFe7oLk5DZRo/ImydXijoZl/4kh2406boRQ7lxTYzbZEEXEhj9NQ==
|
integrity sha512-DyxHC6qPcktwYGKOIy3XqnHRrrXyWR2u91AjP+nLkADko380srsC2DC3s7Y1Rk6YfOlxOlvEQKa9XXmLI+W4ZA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-core" "3.4.35"
|
"@vue/compiler-core" "3.5.10"
|
||||||
"@vue/shared" "3.4.35"
|
"@vue/shared" "3.5.10"
|
||||||
|
|
||||||
"@vue/compiler-dom@^3.4.0":
|
"@vue/compiler-dom@^3.4.0":
|
||||||
version "3.4.21"
|
version "3.4.21"
|
||||||
@ -644,19 +702,19 @@
|
|||||||
"@vue/compiler-core" "3.4.21"
|
"@vue/compiler-core" "3.4.21"
|
||||||
"@vue/shared" "3.4.21"
|
"@vue/shared" "3.4.21"
|
||||||
|
|
||||||
"@vue/compiler-sfc@3.4.35":
|
"@vue/compiler-sfc@3.5.10":
|
||||||
version "3.4.35"
|
version "3.5.10"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.35.tgz#16f87dd3bdab64cef14d3a6fcf53f8673e404071"
|
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.10.tgz#95e262a5ed836521a5aeee9492cc265ad3f1c787"
|
||||||
integrity sha512-xacnRS/h/FCsjsMfxBkzjoNxyxEyKyZfBch/P4vkLRvYJwe5ChXmZZrj8Dsed/752H2Q3JE8kYu9Uyha9J6PgA==
|
integrity sha512-to8E1BgpakV7224ZCm8gz1ZRSyjNCAWEplwFMWKlzCdP9DkMKhRRwt0WkCjY7jkzi/Vz3xgbpeig5Pnbly4Tow==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/parser" "^7.24.7"
|
"@babel/parser" "^7.25.3"
|
||||||
"@vue/compiler-core" "3.4.35"
|
"@vue/compiler-core" "3.5.10"
|
||||||
"@vue/compiler-dom" "3.4.35"
|
"@vue/compiler-dom" "3.5.10"
|
||||||
"@vue/compiler-ssr" "3.4.35"
|
"@vue/compiler-ssr" "3.5.10"
|
||||||
"@vue/shared" "3.4.35"
|
"@vue/shared" "3.5.10"
|
||||||
estree-walker "^2.0.2"
|
estree-walker "^2.0.2"
|
||||||
magic-string "^0.30.10"
|
magic-string "^0.30.11"
|
||||||
postcss "^8.4.40"
|
postcss "^8.4.47"
|
||||||
source-map-js "^1.2.0"
|
source-map-js "^1.2.0"
|
||||||
|
|
||||||
"@vue/compiler-sfc@^3.2.47":
|
"@vue/compiler-sfc@^3.2.47":
|
||||||
@ -683,13 +741,13 @@
|
|||||||
"@vue/compiler-dom" "3.2.47"
|
"@vue/compiler-dom" "3.2.47"
|
||||||
"@vue/shared" "3.2.47"
|
"@vue/shared" "3.2.47"
|
||||||
|
|
||||||
"@vue/compiler-ssr@3.4.35":
|
"@vue/compiler-ssr@3.5.10":
|
||||||
version "3.4.35"
|
version "3.5.10"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.35.tgz#0774c9a0afed74d71615209904b38f3fa9711adb"
|
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.10.tgz#195f83ae7c52174be37fd7a4a0217132c1c0ed11"
|
||||||
integrity sha512-7iynB+0KB1AAJKk/biENTV5cRGHRdbdaD7Mx3nWcm1W8bVD6QmnH3B4AHhQQ1qZHhqFwzEzMwiytXm3PX1e60A==
|
integrity sha512-hxP4Y3KImqdtyUKXDRSxKSRkSm1H9fCvhojEYrnaoWhE4w/y8vwWhnosJoPPe2AXm5sU7CSbYYAgkt2ZPhDz+A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-dom" "3.4.35"
|
"@vue/compiler-dom" "3.5.10"
|
||||||
"@vue/shared" "3.4.35"
|
"@vue/shared" "3.5.10"
|
||||||
|
|
||||||
"@vue/compiler-vue2@^2.7.16":
|
"@vue/compiler-vue2@^2.7.16":
|
||||||
version "2.7.16"
|
version "2.7.16"
|
||||||
@ -700,15 +758,15 @@
|
|||||||
he "^1.2.0"
|
he "^1.2.0"
|
||||||
|
|
||||||
"@vue/devtools-api@^6.5.0":
|
"@vue/devtools-api@^6.5.0":
|
||||||
version "6.5.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.5.0.tgz#98b99425edee70b4c992692628fa1ea2c1e57d07"
|
|
||||||
integrity sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==
|
|
||||||
|
|
||||||
"@vue/devtools-api@^6.6.3":
|
|
||||||
version "6.6.3"
|
version "6.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.6.3.tgz#b23a588154cba8986bba82b6e1d0248bde3fd1a0"
|
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.6.3.tgz#b23a588154cba8986bba82b6e1d0248bde3fd1a0"
|
||||||
integrity sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==
|
integrity sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==
|
||||||
|
|
||||||
|
"@vue/devtools-api@^6.6.4":
|
||||||
|
version "6.6.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz#cbe97fe0162b365edc1dba80e173f90492535343"
|
||||||
|
integrity sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==
|
||||||
|
|
||||||
"@vue/eslint-config-typescript@^13.0.0":
|
"@vue/eslint-config-typescript@^13.0.0":
|
||||||
version "13.0.0"
|
version "13.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/eslint-config-typescript/-/eslint-config-typescript-13.0.0.tgz#f5f3d986ace34a10f403921d5044831b89a1b679"
|
resolved "https://registry.yarnpkg.com/@vue/eslint-config-typescript/-/eslint-config-typescript-13.0.0.tgz#f5f3d986ace34a10f403921d5044831b89a1b679"
|
||||||
@ -718,12 +776,12 @@
|
|||||||
"@typescript-eslint/parser" "^7.1.1"
|
"@typescript-eslint/parser" "^7.1.1"
|
||||||
vue-eslint-parser "^9.3.1"
|
vue-eslint-parser "^9.3.1"
|
||||||
|
|
||||||
"@vue/language-core@2.0.29":
|
"@vue/language-core@2.1.6":
|
||||||
version "2.0.29"
|
version "2.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-2.0.29.tgz#19462d786cd7a1c21dbe575b46970a57094e0357"
|
resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-2.1.6.tgz#b48186bdb9b3ef2b83e1f76d5b1ac357b3a7ed94"
|
||||||
integrity sha512-o2qz9JPjhdoVj8D2+9bDXbaI4q2uZTHQA/dbyZT4Bj1FR9viZxDJnLcKVHfxdn6wsOzRgpqIzJEEmSSvgMvDTQ==
|
integrity sha512-MW569cSky9R/ooKMh6xa2g1D0AtRKbL56k83dzus/bx//RDJk24RHWkMzbAlXjMdDNyxAaagKPRquBIxkxlCkg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@volar/language-core" "~2.4.0-alpha.18"
|
"@volar/language-core" "~2.4.1"
|
||||||
"@vue/compiler-dom" "^3.4.0"
|
"@vue/compiler-dom" "^3.4.0"
|
||||||
"@vue/compiler-vue2" "^2.7.16"
|
"@vue/compiler-vue2" "^2.7.16"
|
||||||
"@vue/shared" "^3.4.0"
|
"@vue/shared" "^3.4.0"
|
||||||
@ -743,38 +801,38 @@
|
|||||||
estree-walker "^2.0.2"
|
estree-walker "^2.0.2"
|
||||||
magic-string "^0.25.7"
|
magic-string "^0.25.7"
|
||||||
|
|
||||||
"@vue/reactivity@3.4.35":
|
"@vue/reactivity@3.5.10":
|
||||||
version "3.4.35"
|
version "3.5.10"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.35.tgz#dfbb4f5371da1290ac86e3313d0e9a68bb0ab38d"
|
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.10.tgz#81140ef0b05096973356d3c8fc32f48c79940b9c"
|
||||||
integrity sha512-Ggtz7ZZHakriKioveJtPlStYardwQH6VCs9V13/4qjHSQb/teE30LVJNrbBVs4+aoYGtTQKJbTe4CWGxVZrvEw==
|
integrity sha512-kW08v06F6xPSHhid9DJ9YjOGmwNDOsJJQk0ax21wKaUYzzuJGEuoKNU2Ujux8FLMrP7CFJJKsHhXN9l2WOVi2g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/shared" "3.4.35"
|
"@vue/shared" "3.5.10"
|
||||||
|
|
||||||
"@vue/runtime-core@3.4.35":
|
"@vue/runtime-core@3.5.10":
|
||||||
version "3.4.35"
|
version "3.5.10"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.35.tgz#c036013a7b1bbe0d14a6b76eb4355dae6690d2e6"
|
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.10.tgz#e902eb2640fa6ab4cc4589af263818a898812668"
|
||||||
integrity sha512-D+BAjFoWwT5wtITpSxwqfWZiBClhBbR+bm0VQlWYFOadUUXFo+5wbe9ErXhLvwguPiLZdEF13QAWi2vP3ZD5tA==
|
integrity sha512-9Q86I5Qq3swSkFfzrZ+iqEy7Vla325M7S7xc1NwKnRm/qoi1Dauz0rT6mTMmscqx4qz0EDJ1wjB+A36k7rl8mA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/reactivity" "3.4.35"
|
"@vue/reactivity" "3.5.10"
|
||||||
"@vue/shared" "3.4.35"
|
"@vue/shared" "3.5.10"
|
||||||
|
|
||||||
"@vue/runtime-dom@3.4.35":
|
"@vue/runtime-dom@3.5.10":
|
||||||
version "3.4.35"
|
version "3.5.10"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.35.tgz#74254c7c327163d692e1d7d2b6d9e92463744e90"
|
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.10.tgz#dca26d7761147373c6929f1370cf2733aa19f3de"
|
||||||
integrity sha512-yGOlbos+MVhlS5NWBF2HDNgblG8e2MY3+GigHEyR/dREAluvI5tuUUgie3/9XeqhPE4LF0i2wjlduh5thnfOqw==
|
integrity sha512-t3x7ht5qF8ZRi1H4fZqFzyY2j+GTMTDxRheT+i8M9Ph0oepUxoadmbwlFwMoW7RYCpNQLpP2Yx3feKs+fyBdpA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/reactivity" "3.4.35"
|
"@vue/reactivity" "3.5.10"
|
||||||
"@vue/runtime-core" "3.4.35"
|
"@vue/runtime-core" "3.5.10"
|
||||||
"@vue/shared" "3.4.35"
|
"@vue/shared" "3.5.10"
|
||||||
csstype "^3.1.3"
|
csstype "^3.1.3"
|
||||||
|
|
||||||
"@vue/server-renderer@3.4.35":
|
"@vue/server-renderer@3.5.10":
|
||||||
version "3.4.35"
|
version "3.5.10"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.35.tgz#188e94e82d8e729ba7b40dd91d10678b85f77c6b"
|
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.10.tgz#90462492c30c8cae499b9149d1b90af2ebfe7599"
|
||||||
integrity sha512-iZ0e/u9mRE4T8tNhlo0tbA+gzVkgv8r5BX6s1kRbOZqfpq14qoIvCZ5gIgraOmYkMYrSEZgkkojFPr+Nyq/Mnw==
|
integrity sha512-IVE97tt2kGKwHNq9yVO0xdh1IvYfZCShvDSy46JIh5OQxP1/EXSpoDqetVmyIzL7CYOWnnmMkVqd7YK2QSWkdw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-ssr" "3.4.35"
|
"@vue/compiler-ssr" "3.5.10"
|
||||||
"@vue/shared" "3.4.35"
|
"@vue/shared" "3.5.10"
|
||||||
|
|
||||||
"@vue/shared@3.2.47":
|
"@vue/shared@3.2.47":
|
||||||
version "3.2.47"
|
version "3.2.47"
|
||||||
@ -786,10 +844,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.21.tgz#de526a9059d0a599f0b429af7037cd0c3ed7d5a1"
|
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.21.tgz#de526a9059d0a599f0b429af7037cd0c3ed7d5a1"
|
||||||
integrity sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==
|
integrity sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==
|
||||||
|
|
||||||
"@vue/shared@3.4.35":
|
"@vue/shared@3.5.10":
|
||||||
version "3.4.35"
|
version "3.5.10"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.35.tgz#5432f4b1c79e763fcf78cc830faf59ff01248968"
|
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.10.tgz#066f7dde31e09d700123e92e63eaa126cda21a17"
|
||||||
integrity sha512-hvuhBYYDe+b1G8KHxsQ0diDqDMA8D9laxWZhNAjE83VZb5UDaXl9Xnz7cGdDSyiHM90qqI/CyGMcpBpiDy6VVQ==
|
integrity sha512-VkkBhU97Ki+XJ0xvl4C9YJsIZ2uIlQ7HqPpZOS3m9VCvmROPaChZU6DexdMJqvz9tbgG+4EtFVrSuailUq5KGQ==
|
||||||
|
|
||||||
"@vue/tsconfig@^0.5.1":
|
"@vue/tsconfig@^0.5.1":
|
||||||
version "0.5.1"
|
version "0.5.1"
|
||||||
@ -1172,17 +1230,17 @@ escodegen@^2.1.0:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
source-map "~0.6.1"
|
source-map "~0.6.1"
|
||||||
|
|
||||||
eslint-plugin-vue@^9.27.0:
|
eslint-plugin-vue@^9.28.0:
|
||||||
version "9.27.0"
|
version "9.28.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.27.0.tgz#c22dae704a03d9ecefa81364ff89f60ce0481f94"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.28.0.tgz#e4412f0c1024bafd15ffeaa6f76f4c99152e2765"
|
||||||
integrity sha512-5Dw3yxEyuBSXTzT5/Ge1X5kIkRTQ3nvBn/VwPwInNiZBSJOO/timWMUaflONnFBzU6NhB68lxnCda7ULV5N7LA==
|
integrity sha512-ShrihdjIhOTxs+MfWun6oJWuk+g/LAhN+CiuOl/jjkG3l0F2AuK5NMTaWqyvBgkFtpYmyks6P4603mLmhNJW8g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/eslint-utils" "^4.4.0"
|
"@eslint-community/eslint-utils" "^4.4.0"
|
||||||
globals "^13.24.0"
|
globals "^13.24.0"
|
||||||
natural-compare "^1.4.0"
|
natural-compare "^1.4.0"
|
||||||
nth-check "^2.1.1"
|
nth-check "^2.1.1"
|
||||||
postcss-selector-parser "^6.0.15"
|
postcss-selector-parser "^6.0.15"
|
||||||
semver "^7.6.0"
|
semver "^7.6.3"
|
||||||
vue-eslint-parser "^9.4.3"
|
vue-eslint-parser "^9.4.3"
|
||||||
xml-name-validator "^4.0.0"
|
xml-name-validator "^4.0.0"
|
||||||
|
|
||||||
@ -1222,19 +1280,23 @@ eslint-visitor-keys@^4.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb"
|
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb"
|
||||||
integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==
|
integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==
|
||||||
|
|
||||||
eslint@^9.8.0:
|
eslint@^9.11.1:
|
||||||
version "9.8.0"
|
version "9.11.1"
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.8.0.tgz#a4f4a090c8ea2d10864d89a6603e02ce9f649f0f"
|
resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.11.1.tgz#701e5fc528990153f9cef696d8427003b5206567"
|
||||||
integrity sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==
|
integrity sha512-MobhYKIoAO1s1e4VUrgx1l1Sk2JBR/Gqjjgw8+mfgoLE2xwsHur4gdfTxyTgShrhvdVFTaJSgMiQBl1jv/AWxg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/eslint-utils" "^4.2.0"
|
"@eslint-community/eslint-utils" "^4.2.0"
|
||||||
"@eslint-community/regexpp" "^4.11.0"
|
"@eslint-community/regexpp" "^4.11.0"
|
||||||
"@eslint/config-array" "^0.17.1"
|
"@eslint/config-array" "^0.18.0"
|
||||||
|
"@eslint/core" "^0.6.0"
|
||||||
"@eslint/eslintrc" "^3.1.0"
|
"@eslint/eslintrc" "^3.1.0"
|
||||||
"@eslint/js" "9.8.0"
|
"@eslint/js" "9.11.1"
|
||||||
|
"@eslint/plugin-kit" "^0.2.0"
|
||||||
"@humanwhocodes/module-importer" "^1.0.1"
|
"@humanwhocodes/module-importer" "^1.0.1"
|
||||||
"@humanwhocodes/retry" "^0.3.0"
|
"@humanwhocodes/retry" "^0.3.0"
|
||||||
"@nodelib/fs.walk" "^1.2.8"
|
"@nodelib/fs.walk" "^1.2.8"
|
||||||
|
"@types/estree" "^1.0.6"
|
||||||
|
"@types/json-schema" "^7.0.15"
|
||||||
ajv "^6.12.4"
|
ajv "^6.12.4"
|
||||||
chalk "^4.0.0"
|
chalk "^4.0.0"
|
||||||
cross-spawn "^7.0.2"
|
cross-spawn "^7.0.2"
|
||||||
@ -1254,7 +1316,6 @@ eslint@^9.8.0:
|
|||||||
is-glob "^4.0.0"
|
is-glob "^4.0.0"
|
||||||
is-path-inside "^3.0.3"
|
is-path-inside "^3.0.3"
|
||||||
json-stable-stringify-without-jsonify "^1.0.1"
|
json-stable-stringify-without-jsonify "^1.0.1"
|
||||||
levn "^0.4.1"
|
|
||||||
lodash.merge "^4.6.2"
|
lodash.merge "^4.6.2"
|
||||||
minimatch "^3.1.2"
|
minimatch "^3.1.2"
|
||||||
natural-compare "^1.4.0"
|
natural-compare "^1.4.0"
|
||||||
@ -1843,12 +1904,12 @@ magic-string@^0.25.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
sourcemap-codec "^1.4.8"
|
sourcemap-codec "^1.4.8"
|
||||||
|
|
||||||
magic-string@^0.30.10:
|
magic-string@^0.30.11:
|
||||||
version "0.30.10"
|
version "0.30.11"
|
||||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.10.tgz#123d9c41a0cb5640c892b041d4cfb3bd0aa4b39e"
|
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.11.tgz#301a6f93b3e8c2cb13ac1a7a673492c0dfd12954"
|
||||||
integrity sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==
|
integrity sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.15"
|
"@jridgewell/sourcemap-codec" "^1.5.0"
|
||||||
|
|
||||||
memorystream@^0.3.1:
|
memorystream@^0.3.1:
|
||||||
version "0.3.1"
|
version "0.3.1"
|
||||||
@ -2072,10 +2133,10 @@ picocolors@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
|
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
|
||||||
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
|
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
|
||||||
|
|
||||||
picocolors@^1.0.1:
|
picocolors@^1.1.0:
|
||||||
version "1.0.1"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1"
|
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59"
|
||||||
integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
|
integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==
|
||||||
|
|
||||||
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
|
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
|
||||||
version "2.3.1"
|
version "2.3.1"
|
||||||
@ -2118,23 +2179,14 @@ postcss@^8.1.10:
|
|||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
postcss@^8.4.39:
|
postcss@^8.4.43, postcss@^8.4.47:
|
||||||
version "8.4.39"
|
version "8.4.47"
|
||||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.39.tgz#aa3c94998b61d3a9c259efa51db4b392e1bde0e3"
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365"
|
||||||
integrity sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==
|
integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
nanoid "^3.3.7"
|
nanoid "^3.3.7"
|
||||||
picocolors "^1.0.1"
|
picocolors "^1.1.0"
|
||||||
source-map-js "^1.2.0"
|
source-map-js "^1.2.1"
|
||||||
|
|
||||||
postcss@^8.4.40:
|
|
||||||
version "8.4.40"
|
|
||||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.40.tgz#eb81f2a4dd7668ed869a6db25999e02e9ad909d8"
|
|
||||||
integrity sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==
|
|
||||||
dependencies:
|
|
||||||
nanoid "^3.3.7"
|
|
||||||
picocolors "^1.0.1"
|
|
||||||
source-map-js "^1.2.0"
|
|
||||||
|
|
||||||
prelude-ls@^1.2.1:
|
prelude-ls@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
@ -2205,26 +2257,29 @@ reusify@^1.0.4:
|
|||||||
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
||||||
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
||||||
|
|
||||||
rollup@^4.13.0:
|
rollup@^4.20.0:
|
||||||
version "4.13.0"
|
version "4.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.13.0.tgz#dd2ae144b4cdc2ea25420477f68d4937a721237a"
|
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.21.2.tgz#f41f277a448d6264e923dd1ea179f0a926aaf9b7"
|
||||||
integrity sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==
|
integrity sha512-e3TapAgYf9xjdLvKQCkQTnbTKd4a6jwlpQSJJFokHGaX2IVjoEqkIIhiQfqsi0cdwlOD+tQGuOd5AJkc5RngBw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/estree" "1.0.5"
|
"@types/estree" "1.0.5"
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
"@rollup/rollup-android-arm-eabi" "4.13.0"
|
"@rollup/rollup-android-arm-eabi" "4.21.2"
|
||||||
"@rollup/rollup-android-arm64" "4.13.0"
|
"@rollup/rollup-android-arm64" "4.21.2"
|
||||||
"@rollup/rollup-darwin-arm64" "4.13.0"
|
"@rollup/rollup-darwin-arm64" "4.21.2"
|
||||||
"@rollup/rollup-darwin-x64" "4.13.0"
|
"@rollup/rollup-darwin-x64" "4.21.2"
|
||||||
"@rollup/rollup-linux-arm-gnueabihf" "4.13.0"
|
"@rollup/rollup-linux-arm-gnueabihf" "4.21.2"
|
||||||
"@rollup/rollup-linux-arm64-gnu" "4.13.0"
|
"@rollup/rollup-linux-arm-musleabihf" "4.21.2"
|
||||||
"@rollup/rollup-linux-arm64-musl" "4.13.0"
|
"@rollup/rollup-linux-arm64-gnu" "4.21.2"
|
||||||
"@rollup/rollup-linux-riscv64-gnu" "4.13.0"
|
"@rollup/rollup-linux-arm64-musl" "4.21.2"
|
||||||
"@rollup/rollup-linux-x64-gnu" "4.13.0"
|
"@rollup/rollup-linux-powerpc64le-gnu" "4.21.2"
|
||||||
"@rollup/rollup-linux-x64-musl" "4.13.0"
|
"@rollup/rollup-linux-riscv64-gnu" "4.21.2"
|
||||||
"@rollup/rollup-win32-arm64-msvc" "4.13.0"
|
"@rollup/rollup-linux-s390x-gnu" "4.21.2"
|
||||||
"@rollup/rollup-win32-ia32-msvc" "4.13.0"
|
"@rollup/rollup-linux-x64-gnu" "4.21.2"
|
||||||
"@rollup/rollup-win32-x64-msvc" "4.13.0"
|
"@rollup/rollup-linux-x64-musl" "4.21.2"
|
||||||
|
"@rollup/rollup-win32-arm64-msvc" "4.21.2"
|
||||||
|
"@rollup/rollup-win32-ia32-msvc" "4.21.2"
|
||||||
|
"@rollup/rollup-win32-x64-msvc" "4.21.2"
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
run-parallel@^1.1.9:
|
run-parallel@^1.1.9:
|
||||||
@ -2271,12 +2326,10 @@ semver@^7.3.6:
|
|||||||
dependencies:
|
dependencies:
|
||||||
lru-cache "^6.0.0"
|
lru-cache "^6.0.0"
|
||||||
|
|
||||||
semver@^7.6.0:
|
semver@^7.6.3:
|
||||||
version "7.6.0"
|
version "7.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
|
||||||
integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
|
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
|
||||||
dependencies:
|
|
||||||
lru-cache "^6.0.0"
|
|
||||||
|
|
||||||
shebang-command@^1.2.0:
|
shebang-command@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
@ -2321,10 +2374,10 @@ slash@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
|
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
|
||||||
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
|
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
|
||||||
|
|
||||||
sortablejs@^1.15.2:
|
sortablejs@^1.15.3:
|
||||||
version "1.15.2"
|
version "1.15.3"
|
||||||
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.2.tgz#4e9f7bda4718bd1838add9f1866ec77169149809"
|
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.3.tgz#033668db5ebfb11167d1249ab88e748f27959e29"
|
||||||
integrity sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA==
|
integrity sha512-zdK3/kwwAK1cJgy1rwl1YtNTbRmc8qW/+vgXf75A7NHag5of4pyI6uK86ktmQETyWRH7IGaE73uZOOBcGxgqZg==
|
||||||
|
|
||||||
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2:
|
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
@ -2336,6 +2389,11 @@ source-map-js@^1.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
|
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
|
||||||
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
|
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
|
||||||
|
|
||||||
|
source-map-js@^1.2.1:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||||
|
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||||
|
|
||||||
source-map-support@~0.5.20:
|
source-map-support@~0.5.20:
|
||||||
version "0.5.21"
|
version "0.5.21"
|
||||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
|
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
|
||||||
@ -2448,10 +2506,10 @@ supports-preserve-symlinks-flag@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||||
|
|
||||||
terser@^5.31.3:
|
terser@^5.34.0:
|
||||||
version "5.31.3"
|
version "5.34.0"
|
||||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.3.tgz#b24b7beb46062f4653f049eea4f0cd165d0f0c38"
|
resolved "https://registry.yarnpkg.com/terser/-/terser-5.34.0.tgz#62f2496542290bc6d8bf886edaee7fac158e37e4"
|
||||||
integrity sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==
|
integrity sha512-y5NUX+U9HhVsK/zihZwoq4r9dICLyV2jXGOriDAVOeKhq3LKVjgJbGO90FisozXLlJfvjHqgckGmJFBb9KYoWQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/source-map" "^0.3.3"
|
"@jridgewell/source-map" "^0.3.3"
|
||||||
acorn "^8.8.2"
|
acorn "^8.8.2"
|
||||||
@ -2463,6 +2521,11 @@ text-table@^0.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||||
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
|
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
|
||||||
|
|
||||||
|
to-fast-properties@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
|
||||||
|
integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==
|
||||||
|
|
||||||
to-regex-range@^5.0.1:
|
to-regex-range@^5.0.1:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
||||||
@ -2487,10 +2550,10 @@ type-fest@^0.20.2:
|
|||||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
|
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
|
||||||
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
|
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
|
||||||
|
|
||||||
typescript@^5.5.4:
|
typescript@^5.6.2:
|
||||||
version "5.5.4"
|
version "5.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.2.tgz#d1de67b6bef77c41823f822df8f0b3bcff60a5a0"
|
||||||
integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==
|
integrity sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==
|
||||||
|
|
||||||
ufo@^1.1.2:
|
ufo@^1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
@ -2507,10 +2570,10 @@ unbox-primitive@^1.0.2:
|
|||||||
has-symbols "^1.0.3"
|
has-symbols "^1.0.3"
|
||||||
which-boxed-primitive "^1.0.2"
|
which-boxed-primitive "^1.0.2"
|
||||||
|
|
||||||
undici-types@~6.13.0:
|
undici-types@~6.19.2:
|
||||||
version "6.13.0"
|
version "6.19.6"
|
||||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.13.0.tgz#e3e79220ab8c81ed1496b5812471afd7cf075ea5"
|
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.6.tgz#e218c3df0987f4c0e0008ca00d6b6472d9b89b36"
|
||||||
integrity sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==
|
integrity sha512-e/vggGopEfTKSvj4ihnOLTsqhrKRN3LeO6qSN/GxohhuRv8qH9bNQ4B8W7e/vFL+0XTnmHPB4/kegunZGA4Org==
|
||||||
|
|
||||||
universalify@^2.0.0:
|
universalify@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
@ -2556,19 +2619,19 @@ vite-plugin-compression@^0.5.1:
|
|||||||
debug "^4.3.3"
|
debug "^4.3.3"
|
||||||
fs-extra "^10.0.0"
|
fs-extra "^10.0.0"
|
||||||
|
|
||||||
vite-plugin-css-injected-by-js@^3.5.1:
|
vite-plugin-css-injected-by-js@^3.5.2:
|
||||||
version "3.5.1"
|
version "3.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.5.1.tgz#b9c568c21b131d08e31aa6d368ee39c9d6c1b6c1"
|
resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.5.2.tgz#1f75d16ad5c05b6b49bf18018099a189ec2e46ad"
|
||||||
integrity sha512-9ioqwDuEBxW55gNoWFEDhfLTrVKXEEZgl5adhWmmqa88EQGKfTmexy4v1Rh0pAS6RhKQs2bUYQArprB32JpUZQ==
|
integrity sha512-2MpU/Y+SCZyWUB6ua3HbJCrgnF0KACAsmzOQt1UvRVJCGF6S8xdA3ZUhWcWdM9ivG4I5az8PnQmwwrkC2CAQrQ==
|
||||||
|
|
||||||
vite@^5.3.5:
|
vite@^5.4.8:
|
||||||
version "5.3.5"
|
version "5.4.8"
|
||||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.3.5.tgz#b847f846fb2b6cb6f6f4ed50a830186138cb83d8"
|
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.8.tgz#af548ce1c211b2785478d3ba3e8da51e39a287e8"
|
||||||
integrity sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==
|
integrity sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild "^0.21.3"
|
esbuild "^0.21.3"
|
||||||
postcss "^8.4.39"
|
postcss "^8.4.43"
|
||||||
rollup "^4.13.0"
|
rollup "^4.20.0"
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.3"
|
fsevents "~2.3.3"
|
||||||
|
|
||||||
@ -2603,7 +2666,7 @@ vue-eslint-parser@^9.4.3:
|
|||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
semver "^7.3.6"
|
semver "^7.3.6"
|
||||||
|
|
||||||
vue-i18n@^9.13.1:
|
vue-i18n@9.13.1:
|
||||||
version "9.13.1"
|
version "9.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.13.1.tgz#a292c8021b7be604ebfca5609ae1f8fafe5c36d7"
|
resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.13.1.tgz#a292c8021b7be604ebfca5609ae1f8fafe5c36d7"
|
||||||
integrity sha512-mh0GIxx0wPtPlcB1q4k277y0iKgo25xmDPWioVVYanjPufDBpvu5ySTjP5wOrSvlYQ2m1xI+CFhGdauv/61uQg==
|
integrity sha512-mh0GIxx0wPtPlcB1q4k277y0iKgo25xmDPWioVVYanjPufDBpvu5ySTjP5wOrSvlYQ2m1xI+CFhGdauv/61uQg==
|
||||||
@ -2612,32 +2675,32 @@ vue-i18n@^9.13.1:
|
|||||||
"@intlify/shared" "9.13.1"
|
"@intlify/shared" "9.13.1"
|
||||||
"@vue/devtools-api" "^6.5.0"
|
"@vue/devtools-api" "^6.5.0"
|
||||||
|
|
||||||
vue-router@^4.4.2:
|
vue-router@^4.4.5:
|
||||||
version "4.4.2"
|
version "4.4.5"
|
||||||
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.4.2.tgz#bc7bf27f108fc15e5cc2a30b314a662275e2b2bb"
|
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.4.5.tgz#bdf535e4cf32414ebdea6b4b403593efdb541388"
|
||||||
integrity sha512-1qNybkn2L7QsLzaXs8nvlQmRKp8XF8DCxZys/Jr1JpQcHsKUxTKzTxCVA1G7NfBfwRIBgCJPoujOG5lHCCNUxw==
|
integrity sha512-4fKZygS8cH1yCyuabAXGUAsyi1b2/o/OKgu/RUb+znIYOxPRxdkytJEx+0wGcpBE1pX6vUgh5jwWOKRGvuA/7Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/devtools-api" "^6.6.3"
|
"@vue/devtools-api" "^6.6.4"
|
||||||
|
|
||||||
vue-tsc@^2.0.29:
|
vue-tsc@^2.1.6:
|
||||||
version "2.0.29"
|
version "2.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-2.0.29.tgz#bf7e9605af9fadec7fd6037d242217f5c6ad2c3b"
|
resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-2.1.6.tgz#d93fdc617da6546674301a746fd7089ea6d4543d"
|
||||||
integrity sha512-MHhsfyxO3mYShZCGYNziSbc63x7cQ5g9kvijV7dRe1TTXBRLxXyL0FnXWpUF1xII2mJ86mwYpYsUmMwkmerq7Q==
|
integrity sha512-f98dyZp5FOukcYmbFpuSCJ4Z0vHSOSmxGttZJCsFeX0M4w/Rsq0s4uKXjcSRsZqsRgQa6z7SfuO+y0HVICE57Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@volar/typescript" "~2.4.0-alpha.18"
|
"@volar/typescript" "~2.4.1"
|
||||||
"@vue/language-core" "2.0.29"
|
"@vue/language-core" "2.1.6"
|
||||||
semver "^7.5.4"
|
semver "^7.5.4"
|
||||||
|
|
||||||
vue@^3.4.35:
|
vue@^3.5.10:
|
||||||
version "3.4.35"
|
version "3.5.10"
|
||||||
resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.35.tgz#9ad23525919eece40153fdf8675d07ddd879eb33"
|
resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.10.tgz#14be9d4655e07be8d5e8295d017815ed14337f96"
|
||||||
integrity sha512-+fl/GLmI4GPileHftVlCdB7fUL4aziPcqTudpTGXCT8s+iZWuOCeNEB5haX6Uz2IpRrbEXOgIFbe+XciCuGbNQ==
|
integrity sha512-Vy2kmJwHPlouC/tSnIgXVg03SG+9wSqT1xu1Vehc+ChsXsRd7jLkKgMltVEFOzUdBr3uFwBCG+41LJtfAcBRng==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/compiler-dom" "3.4.35"
|
"@vue/compiler-dom" "3.5.10"
|
||||||
"@vue/compiler-sfc" "3.4.35"
|
"@vue/compiler-sfc" "3.5.10"
|
||||||
"@vue/runtime-dom" "3.4.35"
|
"@vue/runtime-dom" "3.5.10"
|
||||||
"@vue/server-renderer" "3.4.35"
|
"@vue/server-renderer" "3.5.10"
|
||||||
"@vue/shared" "3.4.35"
|
"@vue/shared" "3.5.10"
|
||||||
|
|
||||||
webpack-sources@^3.2.3:
|
webpack-sources@^3.2.3:
|
||||||
version "3.2.3"
|
version "3.2.3"
|
||||||
|
|||||||
Binary file not shown.
Loading…
Reference in New Issue
Block a user