mirror of
https://github.com/esphome/esphome.git
synced 2025-08-02 08:27:47 +00:00
Merge branch 'integration' into memory_api
This commit is contained in:
commit
6b83975df0
@ -1 +1 @@
|
|||||||
07f621354fe1350ba51953c80273cd44a04aa44f15cc30bd7b8fe2a641427b7a
|
0c2acbc16bfb7d63571dbe7042f94f683be25e4ca8a0f158a960a94adac4b931
|
||||||
|
3
.github/workflows/auto-label-pr.yml
vendored
3
.github/workflows/auto-label-pr.yml
vendored
@ -305,8 +305,7 @@ jobs:
|
|||||||
const { data: codeownersFile } = await github.rest.repos.getContent({
|
const { data: codeownersFile } = await github.rest.repos.getContent({
|
||||||
owner,
|
owner,
|
||||||
repo,
|
repo,
|
||||||
path: '.github/CODEOWNERS',
|
path: 'CODEOWNERS',
|
||||||
ref: context.payload.pull_request.head.sha
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const codeownersContent = Buffer.from(codeownersFile.content, 'base64').toString('utf8');
|
const codeownersContent = Buffer.from(codeownersFile.content, 'base64').toString('utf8');
|
||||||
|
264
.github/workflows/codeowner-review-request.yml
vendored
Normal file
264
.github/workflows/codeowner-review-request.yml
vendored
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
# This workflow automatically requests reviews from codeowners when:
|
||||||
|
# 1. A PR is opened, reopened, or synchronized (updated)
|
||||||
|
# 2. A PR is marked as ready for review
|
||||||
|
#
|
||||||
|
# It reads the CODEOWNERS file and matches all changed files in the PR against
|
||||||
|
# the codeowner patterns, then requests reviews from the appropriate owners
|
||||||
|
# while avoiding duplicate requests for users who have already been requested
|
||||||
|
# or have already reviewed the PR.
|
||||||
|
|
||||||
|
name: Request Codeowner Reviews
|
||||||
|
|
||||||
|
on:
|
||||||
|
# Needs to be pull_request_target to get write permissions
|
||||||
|
pull_request_target:
|
||||||
|
types: [opened, reopened, synchronize, ready_for_review]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
request-codeowner-reviews:
|
||||||
|
name: Run
|
||||||
|
if: ${{ !github.event.pull_request.draft }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Request reviews from component codeowners
|
||||||
|
uses: actions/github-script@v7.0.1
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const owner = context.repo.owner;
|
||||||
|
const repo = context.repo.repo;
|
||||||
|
const pr_number = context.payload.pull_request.number;
|
||||||
|
|
||||||
|
console.log(`Processing PR #${pr_number} for codeowner review requests`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get the list of changed files in this PR
|
||||||
|
const { data: files } = await github.rest.pulls.listFiles({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
pull_number: pr_number
|
||||||
|
});
|
||||||
|
|
||||||
|
const changedFiles = files.map(file => file.filename);
|
||||||
|
console.log(`Found ${changedFiles.length} changed files`);
|
||||||
|
|
||||||
|
if (changedFiles.length === 0) {
|
||||||
|
console.log('No changed files found, skipping codeowner review requests');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch CODEOWNERS file from root
|
||||||
|
const { data: codeownersFile } = await github.rest.repos.getContent({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
path: 'CODEOWNERS',
|
||||||
|
ref: context.payload.pull_request.base.sha
|
||||||
|
});
|
||||||
|
const codeownersContent = Buffer.from(codeownersFile.content, 'base64').toString('utf8');
|
||||||
|
|
||||||
|
// Parse CODEOWNERS file to extract all patterns and their owners
|
||||||
|
const codeownersLines = codeownersContent.split('\n')
|
||||||
|
.map(line => line.trim())
|
||||||
|
.filter(line => line && !line.startsWith('#'));
|
||||||
|
|
||||||
|
const codeownersPatterns = [];
|
||||||
|
|
||||||
|
// Convert CODEOWNERS pattern to regex (robust glob handling)
|
||||||
|
function globToRegex(pattern) {
|
||||||
|
// Escape regex special characters except for glob wildcards
|
||||||
|
let regexStr = pattern
|
||||||
|
.replace(/([.+^=!:${}()|[\]\\])/g, '\\$1') // escape regex chars
|
||||||
|
.replace(/\*\*/g, '.*') // globstar
|
||||||
|
.replace(/\*/g, '[^/]*') // single star
|
||||||
|
.replace(/\?/g, '.'); // question mark
|
||||||
|
return new RegExp('^' + regexStr + '$');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to create comment body
|
||||||
|
function createCommentBody(reviewersList, teamsList, matchedFileCount, isSuccessful = true) {
|
||||||
|
const reviewerMentions = reviewersList.map(r => `@${r}`);
|
||||||
|
const teamMentions = teamsList.map(t => `@${owner}/${t}`);
|
||||||
|
const allMentions = [...reviewerMentions, ...teamMentions].join(', ');
|
||||||
|
|
||||||
|
if (isSuccessful) {
|
||||||
|
return `👋 Hi there! I've automatically requested reviews from codeowners based on the files changed in this PR.\n\n${allMentions} - You've been requested to review this PR as codeowner(s) of ${matchedFileCount} file(s) that were modified. Thanks for your time! 🙏`;
|
||||||
|
} else {
|
||||||
|
return `👋 Hi there! This PR modifies ${matchedFileCount} file(s) with codeowners.\n\n${allMentions} - As codeowner(s) of the affected files, your review would be appreciated! 🙏\n\n_Note: Automatic review request may have failed, but you're still welcome to review._`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const line of codeownersLines) {
|
||||||
|
const parts = line.split(/\s+/);
|
||||||
|
if (parts.length < 2) continue;
|
||||||
|
|
||||||
|
const pattern = parts[0];
|
||||||
|
const owners = parts.slice(1);
|
||||||
|
|
||||||
|
// Use robust glob-to-regex conversion
|
||||||
|
const regex = globToRegex(pattern);
|
||||||
|
codeownersPatterns.push({ pattern, regex, owners });
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Parsed ${codeownersPatterns.length} codeowner patterns`);
|
||||||
|
|
||||||
|
// Match changed files against CODEOWNERS patterns
|
||||||
|
const matchedOwners = new Set();
|
||||||
|
const matchedTeams = new Set();
|
||||||
|
const fileMatches = new Map(); // Track which files matched which patterns
|
||||||
|
|
||||||
|
for (const file of changedFiles) {
|
||||||
|
for (const { pattern, regex, owners } of codeownersPatterns) {
|
||||||
|
if (regex.test(file)) {
|
||||||
|
console.log(`File '${file}' matches pattern '${pattern}' with owners: ${owners.join(', ')}`);
|
||||||
|
|
||||||
|
if (!fileMatches.has(file)) {
|
||||||
|
fileMatches.set(file, []);
|
||||||
|
}
|
||||||
|
fileMatches.get(file).push({ pattern, owners });
|
||||||
|
|
||||||
|
// Add owners to the appropriate set (remove @ prefix)
|
||||||
|
for (const owner of owners) {
|
||||||
|
const cleanOwner = owner.startsWith('@') ? owner.slice(1) : owner;
|
||||||
|
if (cleanOwner.includes('/')) {
|
||||||
|
// Team mention (org/team-name)
|
||||||
|
const teamName = cleanOwner.split('/')[1];
|
||||||
|
matchedTeams.add(teamName);
|
||||||
|
} else {
|
||||||
|
// Individual user
|
||||||
|
matchedOwners.add(cleanOwner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchedOwners.size === 0 && matchedTeams.size === 0) {
|
||||||
|
console.log('No codeowners found for any changed files');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the PR author from reviewers
|
||||||
|
const prAuthor = context.payload.pull_request.user.login;
|
||||||
|
matchedOwners.delete(prAuthor);
|
||||||
|
|
||||||
|
// Get current reviewers to avoid duplicate requests (but still mention them)
|
||||||
|
const { data: prData } = await github.rest.pulls.get({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
pull_number: pr_number
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentReviewers = new Set();
|
||||||
|
const currentTeams = new Set();
|
||||||
|
|
||||||
|
if (prData.requested_reviewers) {
|
||||||
|
prData.requested_reviewers.forEach(reviewer => {
|
||||||
|
currentReviewers.add(reviewer.login);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prData.requested_teams) {
|
||||||
|
prData.requested_teams.forEach(team => {
|
||||||
|
currentTeams.add(team.slug);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for completed reviews to avoid re-requesting users who have already reviewed
|
||||||
|
const { data: reviews } = await github.rest.pulls.listReviews({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
pull_number: pr_number
|
||||||
|
});
|
||||||
|
|
||||||
|
const reviewedUsers = new Set();
|
||||||
|
reviews.forEach(review => {
|
||||||
|
reviewedUsers.add(review.user.login);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove only users who have already submitted reviews (not just requested reviewers)
|
||||||
|
reviewedUsers.forEach(reviewer => {
|
||||||
|
matchedOwners.delete(reviewer);
|
||||||
|
});
|
||||||
|
|
||||||
|
// For teams, we'll still remove already requested teams to avoid API errors
|
||||||
|
currentTeams.forEach(team => {
|
||||||
|
matchedTeams.delete(team);
|
||||||
|
});
|
||||||
|
|
||||||
|
const reviewersList = Array.from(matchedOwners);
|
||||||
|
const teamsList = Array.from(matchedTeams);
|
||||||
|
|
||||||
|
if (reviewersList.length === 0 && teamsList.length === 0) {
|
||||||
|
console.log('No eligible reviewers found (all may already be requested or reviewed)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalReviewers = reviewersList.length + teamsList.length;
|
||||||
|
console.log(`Requesting reviews from ${reviewersList.length} users and ${teamsList.length} teams for ${fileMatches.size} matched files`);
|
||||||
|
|
||||||
|
// Request reviews
|
||||||
|
try {
|
||||||
|
const requestParams = {
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
pull_number: pr_number
|
||||||
|
};
|
||||||
|
|
||||||
|
// Filter out users who are already requested reviewers for the API call
|
||||||
|
const newReviewers = reviewersList.filter(reviewer => !currentReviewers.has(reviewer));
|
||||||
|
const newTeams = teamsList.filter(team => !currentTeams.has(team));
|
||||||
|
|
||||||
|
if (newReviewers.length > 0) {
|
||||||
|
requestParams.reviewers = newReviewers;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newTeams.length > 0) {
|
||||||
|
requestParams.team_reviewers = newTeams;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only make the API call if there are new reviewers to request
|
||||||
|
if (newReviewers.length > 0 || newTeams.length > 0) {
|
||||||
|
await github.rest.pulls.requestReviewers(requestParams);
|
||||||
|
console.log(`Successfully requested reviews from ${newReviewers.length} new users and ${newTeams.length} new teams`);
|
||||||
|
} else {
|
||||||
|
console.log('All codeowners are already requested reviewers or have reviewed');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a comment to the PR mentioning what happened (include all matched codeowners)
|
||||||
|
const commentBody = createCommentBody(reviewersList, teamsList, fileMatches.size, true);
|
||||||
|
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
issue_number: pr_number,
|
||||||
|
body: commentBody
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
if (error.status === 422) {
|
||||||
|
console.log('Some reviewers may already be requested or unavailable:', error.message);
|
||||||
|
|
||||||
|
// Try to add a comment even if review request failed
|
||||||
|
const commentBody = createCommentBody(reviewersList, teamsList, fileMatches.size, false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
issue_number: pr_number,
|
||||||
|
body: commentBody
|
||||||
|
});
|
||||||
|
} catch (commentError) {
|
||||||
|
console.log('Failed to add comment:', commentError.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Failed to process codeowner review requests:', error.message);
|
||||||
|
console.error(error);
|
||||||
|
}
|
119
.github/workflows/issue-codeowner-notify.yml
vendored
Normal file
119
.github/workflows/issue-codeowner-notify.yml
vendored
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
# This workflow automatically notifies codeowners when an issue is labeled with component labels.
|
||||||
|
# It reads the CODEOWNERS file to find the maintainers for the labeled components
|
||||||
|
# and posts a comment mentioning them to ensure they're aware of the issue.
|
||||||
|
|
||||||
|
name: Notify Issue Codeowners
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [labeled]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
notify-codeowners:
|
||||||
|
name: Run
|
||||||
|
if: ${{ startsWith(github.event.label.name, format('component{0} ', ':')) }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Notify codeowners for component issues
|
||||||
|
uses: actions/github-script@v7.0.1
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const owner = context.repo.owner;
|
||||||
|
const repo = context.repo.repo;
|
||||||
|
const issue_number = context.payload.issue.number;
|
||||||
|
const labelName = context.payload.label.name;
|
||||||
|
|
||||||
|
console.log(`Processing issue #${issue_number} with label: ${labelName}`);
|
||||||
|
|
||||||
|
// Extract component name from label
|
||||||
|
const componentName = labelName.replace('component: ', '');
|
||||||
|
console.log(`Component: ${componentName}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fetch CODEOWNERS file from root
|
||||||
|
const { data: codeownersFile } = await github.rest.repos.getContent({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
path: 'CODEOWNERS'
|
||||||
|
});
|
||||||
|
const codeownersContent = Buffer.from(codeownersFile.content, 'base64').toString('utf8');
|
||||||
|
|
||||||
|
// Parse CODEOWNERS file to extract component mappings
|
||||||
|
const codeownersLines = codeownersContent.split('\n')
|
||||||
|
.map(line => line.trim())
|
||||||
|
.filter(line => line && !line.startsWith('#'));
|
||||||
|
|
||||||
|
let componentOwners = null;
|
||||||
|
|
||||||
|
for (const line of codeownersLines) {
|
||||||
|
const parts = line.split(/\s+/);
|
||||||
|
if (parts.length < 2) continue;
|
||||||
|
|
||||||
|
const pattern = parts[0];
|
||||||
|
const owners = parts.slice(1);
|
||||||
|
|
||||||
|
// Look for component patterns: esphome/components/{component}/*
|
||||||
|
const componentMatch = pattern.match(/^esphome\/components\/([^\/]+)\/\*$/);
|
||||||
|
if (componentMatch && componentMatch[1] === componentName) {
|
||||||
|
componentOwners = owners;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!componentOwners) {
|
||||||
|
console.log(`No codeowners found for component: ${componentName}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Found codeowners for '${componentName}': ${componentOwners.join(', ')}`);
|
||||||
|
|
||||||
|
// Separate users and teams
|
||||||
|
const userOwners = [];
|
||||||
|
const teamOwners = [];
|
||||||
|
|
||||||
|
for (const owner of componentOwners) {
|
||||||
|
const cleanOwner = owner.startsWith('@') ? owner.slice(1) : owner;
|
||||||
|
if (cleanOwner.includes('/')) {
|
||||||
|
// Team mention (org/team-name)
|
||||||
|
teamOwners.push(`@${cleanOwner}`);
|
||||||
|
} else {
|
||||||
|
// Individual user
|
||||||
|
userOwners.push(`@${cleanOwner}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove issue author from mentions to avoid self-notification
|
||||||
|
const issueAuthor = context.payload.issue.user.login;
|
||||||
|
const filteredUserOwners = userOwners.filter(mention =>
|
||||||
|
mention !== `@${issueAuthor}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const allMentions = [...filteredUserOwners, ...teamOwners];
|
||||||
|
|
||||||
|
if (allMentions.length === 0) {
|
||||||
|
console.log('No codeowners to notify (issue author is the only codeowner)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create comment body
|
||||||
|
const mentionString = allMentions.join(', ');
|
||||||
|
const commentBody = `👋 Hey ${mentionString}!\n\nThis issue has been labeled with \`component: ${componentName}\` and you've been identified as a codeowner of this component. Please take a look when you have a chance!\n\nThanks for maintaining this component! 🙏`;
|
||||||
|
|
||||||
|
// Post comment
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
issue_number: issue_number,
|
||||||
|
body: commentBody
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Successfully notified codeowners: ${mentionString}`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Failed to process codeowner notifications:', error.message);
|
||||||
|
console.error(error);
|
||||||
|
}
|
@ -230,14 +230,16 @@ message DeviceInfoResponse {
|
|||||||
|
|
||||||
uint32 webserver_port = 10 [(field_ifdef) = "USE_WEBSERVER"];
|
uint32 webserver_port = 10 [(field_ifdef) = "USE_WEBSERVER"];
|
||||||
|
|
||||||
uint32 legacy_bluetooth_proxy_version = 11 [(field_ifdef) = "USE_BLUETOOTH_PROXY"];
|
// Deprecated in API version 1.9
|
||||||
|
uint32 legacy_bluetooth_proxy_version = 11 [deprecated=true, (field_ifdef) = "USE_BLUETOOTH_PROXY"];
|
||||||
uint32 bluetooth_proxy_feature_flags = 15 [(field_ifdef) = "USE_BLUETOOTH_PROXY"];
|
uint32 bluetooth_proxy_feature_flags = 15 [(field_ifdef) = "USE_BLUETOOTH_PROXY"];
|
||||||
|
|
||||||
string manufacturer = 12;
|
string manufacturer = 12;
|
||||||
|
|
||||||
string friendly_name = 13;
|
string friendly_name = 13;
|
||||||
|
|
||||||
uint32 legacy_voice_assistant_version = 14 [(field_ifdef) = "USE_VOICE_ASSISTANT"];
|
// Deprecated in API version 1.10
|
||||||
|
uint32 legacy_voice_assistant_version = 14 [deprecated=true, (field_ifdef) = "USE_VOICE_ASSISTANT"];
|
||||||
uint32 voice_assistant_feature_flags = 17 [(field_ifdef) = "USE_VOICE_ASSISTANT"];
|
uint32 voice_assistant_feature_flags = 17 [(field_ifdef) = "USE_VOICE_ASSISTANT"];
|
||||||
|
|
||||||
string suggested_area = 16 [(field_ifdef) = "USE_AREAS"];
|
string suggested_area = 16 [(field_ifdef) = "USE_AREAS"];
|
||||||
@ -337,7 +339,9 @@ message ListEntitiesCoverResponse {
|
|||||||
uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"];
|
uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated in API version 1.1
|
||||||
enum LegacyCoverState {
|
enum LegacyCoverState {
|
||||||
|
option deprecated = true;
|
||||||
LEGACY_COVER_STATE_OPEN = 0;
|
LEGACY_COVER_STATE_OPEN = 0;
|
||||||
LEGACY_COVER_STATE_CLOSED = 1;
|
LEGACY_COVER_STATE_CLOSED = 1;
|
||||||
}
|
}
|
||||||
@ -356,7 +360,8 @@ message CoverStateResponse {
|
|||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
// legacy: state has been removed in 1.13
|
// legacy: state has been removed in 1.13
|
||||||
// clients/servers must still send/accept it until the next protocol change
|
// clients/servers must still send/accept it until the next protocol change
|
||||||
LegacyCoverState legacy_state = 2;
|
// Deprecated in API version 1.1
|
||||||
|
LegacyCoverState legacy_state = 2 [deprecated=true];
|
||||||
|
|
||||||
float position = 3;
|
float position = 3;
|
||||||
float tilt = 4;
|
float tilt = 4;
|
||||||
@ -364,7 +369,9 @@ message CoverStateResponse {
|
|||||||
uint32 device_id = 6 [(field_ifdef) = "USE_DEVICES"];
|
uint32 device_id = 6 [(field_ifdef) = "USE_DEVICES"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated in API version 1.1
|
||||||
enum LegacyCoverCommand {
|
enum LegacyCoverCommand {
|
||||||
|
option deprecated = true;
|
||||||
LEGACY_COVER_COMMAND_OPEN = 0;
|
LEGACY_COVER_COMMAND_OPEN = 0;
|
||||||
LEGACY_COVER_COMMAND_CLOSE = 1;
|
LEGACY_COVER_COMMAND_CLOSE = 1;
|
||||||
LEGACY_COVER_COMMAND_STOP = 2;
|
LEGACY_COVER_COMMAND_STOP = 2;
|
||||||
@ -380,8 +387,10 @@ message CoverCommandRequest {
|
|||||||
|
|
||||||
// legacy: command has been removed in 1.13
|
// legacy: command has been removed in 1.13
|
||||||
// clients/servers must still send/accept it until the next protocol change
|
// clients/servers must still send/accept it until the next protocol change
|
||||||
bool has_legacy_command = 2;
|
// Deprecated in API version 1.1
|
||||||
LegacyCoverCommand legacy_command = 3;
|
bool has_legacy_command = 2 [deprecated=true];
|
||||||
|
// Deprecated in API version 1.1
|
||||||
|
LegacyCoverCommand legacy_command = 3 [deprecated=true];
|
||||||
|
|
||||||
bool has_position = 4;
|
bool has_position = 4;
|
||||||
float position = 5;
|
float position = 5;
|
||||||
@ -413,7 +422,9 @@ message ListEntitiesFanResponse {
|
|||||||
repeated string supported_preset_modes = 12;
|
repeated string supported_preset_modes = 12;
|
||||||
uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"];
|
uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"];
|
||||||
}
|
}
|
||||||
|
// Deprecated in API version 1.6 - only used in deprecated fields
|
||||||
enum FanSpeed {
|
enum FanSpeed {
|
||||||
|
option deprecated = true;
|
||||||
FAN_SPEED_LOW = 0;
|
FAN_SPEED_LOW = 0;
|
||||||
FAN_SPEED_MEDIUM = 1;
|
FAN_SPEED_MEDIUM = 1;
|
||||||
FAN_SPEED_HIGH = 2;
|
FAN_SPEED_HIGH = 2;
|
||||||
@ -432,7 +443,8 @@ message FanStateResponse {
|
|||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
bool state = 2;
|
bool state = 2;
|
||||||
bool oscillating = 3;
|
bool oscillating = 3;
|
||||||
FanSpeed speed = 4 [deprecated = true];
|
// Deprecated in API version 1.6
|
||||||
|
FanSpeed speed = 4 [deprecated=true];
|
||||||
FanDirection direction = 5;
|
FanDirection direction = 5;
|
||||||
int32 speed_level = 6;
|
int32 speed_level = 6;
|
||||||
string preset_mode = 7;
|
string preset_mode = 7;
|
||||||
@ -448,8 +460,10 @@ message FanCommandRequest {
|
|||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
bool has_state = 2;
|
bool has_state = 2;
|
||||||
bool state = 3;
|
bool state = 3;
|
||||||
bool has_speed = 4 [deprecated = true];
|
// Deprecated in API version 1.6
|
||||||
FanSpeed speed = 5 [deprecated = true];
|
bool has_speed = 4 [deprecated=true];
|
||||||
|
// Deprecated in API version 1.6
|
||||||
|
FanSpeed speed = 5 [deprecated=true];
|
||||||
bool has_oscillating = 6;
|
bool has_oscillating = 6;
|
||||||
bool oscillating = 7;
|
bool oscillating = 7;
|
||||||
bool has_direction = 8;
|
bool has_direction = 8;
|
||||||
@ -488,9 +502,13 @@ message ListEntitiesLightResponse {
|
|||||||
|
|
||||||
repeated ColorMode supported_color_modes = 12;
|
repeated ColorMode supported_color_modes = 12;
|
||||||
// next four supports_* are for legacy clients, newer clients should use color modes
|
// next four supports_* are for legacy clients, newer clients should use color modes
|
||||||
|
// Deprecated in API version 1.6
|
||||||
bool legacy_supports_brightness = 5 [deprecated=true];
|
bool legacy_supports_brightness = 5 [deprecated=true];
|
||||||
|
// Deprecated in API version 1.6
|
||||||
bool legacy_supports_rgb = 6 [deprecated=true];
|
bool legacy_supports_rgb = 6 [deprecated=true];
|
||||||
|
// Deprecated in API version 1.6
|
||||||
bool legacy_supports_white_value = 7 [deprecated=true];
|
bool legacy_supports_white_value = 7 [deprecated=true];
|
||||||
|
// Deprecated in API version 1.6
|
||||||
bool legacy_supports_color_temperature = 8 [deprecated=true];
|
bool legacy_supports_color_temperature = 8 [deprecated=true];
|
||||||
float min_mireds = 9;
|
float min_mireds = 9;
|
||||||
float max_mireds = 10;
|
float max_mireds = 10;
|
||||||
@ -567,7 +585,9 @@ enum SensorStateClass {
|
|||||||
STATE_CLASS_TOTAL = 3;
|
STATE_CLASS_TOTAL = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated in API version 1.5
|
||||||
enum SensorLastResetType {
|
enum SensorLastResetType {
|
||||||
|
option deprecated = true;
|
||||||
LAST_RESET_NONE = 0;
|
LAST_RESET_NONE = 0;
|
||||||
LAST_RESET_NEVER = 1;
|
LAST_RESET_NEVER = 1;
|
||||||
LAST_RESET_AUTO = 2;
|
LAST_RESET_AUTO = 2;
|
||||||
@ -591,7 +611,8 @@ message ListEntitiesSensorResponse {
|
|||||||
string device_class = 9;
|
string device_class = 9;
|
||||||
SensorStateClass state_class = 10;
|
SensorStateClass state_class = 10;
|
||||||
// Last reset type removed in 2021.9.0
|
// Last reset type removed in 2021.9.0
|
||||||
SensorLastResetType legacy_last_reset_type = 11;
|
// Deprecated in API version 1.5
|
||||||
|
SensorLastResetType legacy_last_reset_type = 11 [deprecated=true];
|
||||||
bool disabled_by_default = 12;
|
bool disabled_by_default = 12;
|
||||||
EntityCategory entity_category = 13;
|
EntityCategory entity_category = 13;
|
||||||
uint32 device_id = 14 [(field_ifdef) = "USE_DEVICES"];
|
uint32 device_id = 14 [(field_ifdef) = "USE_DEVICES"];
|
||||||
@ -947,7 +968,8 @@ message ListEntitiesClimateResponse {
|
|||||||
float visual_target_temperature_step = 10;
|
float visual_target_temperature_step = 10;
|
||||||
// for older peer versions - in new system this
|
// for older peer versions - in new system this
|
||||||
// is if CLIMATE_PRESET_AWAY exists is supported_presets
|
// is if CLIMATE_PRESET_AWAY exists is supported_presets
|
||||||
bool legacy_supports_away = 11;
|
// Deprecated in API version 1.5
|
||||||
|
bool legacy_supports_away = 11 [deprecated=true];
|
||||||
bool supports_action = 12;
|
bool supports_action = 12;
|
||||||
repeated ClimateFanMode supported_fan_modes = 13;
|
repeated ClimateFanMode supported_fan_modes = 13;
|
||||||
repeated ClimateSwingMode supported_swing_modes = 14;
|
repeated ClimateSwingMode supported_swing_modes = 14;
|
||||||
@ -978,7 +1000,8 @@ message ClimateStateResponse {
|
|||||||
float target_temperature_low = 5;
|
float target_temperature_low = 5;
|
||||||
float target_temperature_high = 6;
|
float target_temperature_high = 6;
|
||||||
// For older peers, equal to preset == CLIMATE_PRESET_AWAY
|
// For older peers, equal to preset == CLIMATE_PRESET_AWAY
|
||||||
bool unused_legacy_away = 7;
|
// Deprecated in API version 1.5
|
||||||
|
bool unused_legacy_away = 7 [deprecated=true];
|
||||||
ClimateAction action = 8;
|
ClimateAction action = 8;
|
||||||
ClimateFanMode fan_mode = 9;
|
ClimateFanMode fan_mode = 9;
|
||||||
ClimateSwingMode swing_mode = 10;
|
ClimateSwingMode swing_mode = 10;
|
||||||
@ -1006,8 +1029,10 @@ message ClimateCommandRequest {
|
|||||||
bool has_target_temperature_high = 8;
|
bool has_target_temperature_high = 8;
|
||||||
float target_temperature_high = 9;
|
float target_temperature_high = 9;
|
||||||
// legacy, for older peers, newer ones should use CLIMATE_PRESET_AWAY in preset
|
// legacy, for older peers, newer ones should use CLIMATE_PRESET_AWAY in preset
|
||||||
bool unused_has_legacy_away = 10;
|
// Deprecated in API version 1.5
|
||||||
bool unused_legacy_away = 11;
|
bool unused_has_legacy_away = 10 [deprecated=true];
|
||||||
|
// Deprecated in API version 1.5
|
||||||
|
bool unused_legacy_away = 11 [deprecated=true];
|
||||||
bool has_fan_mode = 12;
|
bool has_fan_mode = 12;
|
||||||
ClimateFanMode fan_mode = 13;
|
ClimateFanMode fan_mode = 13;
|
||||||
bool has_swing_mode = 14;
|
bool has_swing_mode = 14;
|
||||||
@ -1354,12 +1379,17 @@ message SubscribeBluetoothLEAdvertisementsRequest {
|
|||||||
uint32 flags = 1;
|
uint32 flags = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated - only used by deprecated BluetoothLEAdvertisementResponse
|
||||||
message BluetoothServiceData {
|
message BluetoothServiceData {
|
||||||
|
option deprecated = true;
|
||||||
string uuid = 1;
|
string uuid = 1;
|
||||||
repeated uint32 legacy_data = 2 [deprecated = true]; // Removed in api version 1.7
|
// Deprecated in API version 1.7
|
||||||
|
repeated uint32 legacy_data = 2 [deprecated=true]; // Removed in api version 1.7
|
||||||
bytes data = 3; // Added in api version 1.7
|
bytes data = 3; // Added in api version 1.7
|
||||||
}
|
}
|
||||||
|
// Removed in ESPHome 2025.8.0 - use BluetoothLERawAdvertisementsResponse instead
|
||||||
message BluetoothLEAdvertisementResponse {
|
message BluetoothLEAdvertisementResponse {
|
||||||
|
option deprecated = true;
|
||||||
option (id) = 67;
|
option (id) = 67;
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_BLUETOOTH_PROXY";
|
option (ifdef) = "USE_BLUETOOTH_PROXY";
|
||||||
|
@ -362,8 +362,6 @@ uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *
|
|||||||
auto *cover = static_cast<cover::Cover *>(entity);
|
auto *cover = static_cast<cover::Cover *>(entity);
|
||||||
CoverStateResponse msg;
|
CoverStateResponse msg;
|
||||||
auto traits = cover->get_traits();
|
auto traits = cover->get_traits();
|
||||||
msg.legacy_state =
|
|
||||||
(cover->position == cover::COVER_OPEN) ? enums::LEGACY_COVER_STATE_OPEN : enums::LEGACY_COVER_STATE_CLOSED;
|
|
||||||
msg.position = cover->position;
|
msg.position = cover->position;
|
||||||
if (traits.get_supports_tilt())
|
if (traits.get_supports_tilt())
|
||||||
msg.tilt = cover->tilt;
|
msg.tilt = cover->tilt;
|
||||||
@ -385,19 +383,6 @@ uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *c
|
|||||||
}
|
}
|
||||||
void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
||||||
ENTITY_COMMAND_MAKE_CALL(cover::Cover, cover, cover)
|
ENTITY_COMMAND_MAKE_CALL(cover::Cover, cover, cover)
|
||||||
if (msg.has_legacy_command) {
|
|
||||||
switch (msg.legacy_command) {
|
|
||||||
case enums::LEGACY_COVER_COMMAND_OPEN:
|
|
||||||
call.set_command_open();
|
|
||||||
break;
|
|
||||||
case enums::LEGACY_COVER_COMMAND_CLOSE:
|
|
||||||
call.set_command_close();
|
|
||||||
break;
|
|
||||||
case enums::LEGACY_COVER_COMMAND_STOP:
|
|
||||||
call.set_command_stop();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (msg.has_position)
|
if (msg.has_position)
|
||||||
call.set_position(msg.position);
|
call.set_position(msg.position);
|
||||||
if (msg.has_tilt)
|
if (msg.has_tilt)
|
||||||
@ -495,14 +480,8 @@ uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *c
|
|||||||
auto traits = light->get_traits();
|
auto traits = light->get_traits();
|
||||||
for (auto mode : traits.get_supported_color_modes())
|
for (auto mode : traits.get_supported_color_modes())
|
||||||
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
|
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
|
||||||
msg.legacy_supports_brightness = traits.supports_color_capability(light::ColorCapability::BRIGHTNESS);
|
if (traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) ||
|
||||||
msg.legacy_supports_rgb = traits.supports_color_capability(light::ColorCapability::RGB);
|
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE)) {
|
||||||
msg.legacy_supports_white_value =
|
|
||||||
msg.legacy_supports_rgb && (traits.supports_color_capability(light::ColorCapability::WHITE) ||
|
|
||||||
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE));
|
|
||||||
msg.legacy_supports_color_temperature = traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) ||
|
|
||||||
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE);
|
|
||||||
if (msg.legacy_supports_color_temperature) {
|
|
||||||
msg.min_mireds = traits.get_min_mireds();
|
msg.min_mireds = traits.get_min_mireds();
|
||||||
msg.max_mireds = traits.get_max_mireds();
|
msg.max_mireds = traits.get_max_mireds();
|
||||||
}
|
}
|
||||||
@ -692,7 +671,6 @@ uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection
|
|||||||
msg.visual_current_temperature_step = traits.get_visual_current_temperature_step();
|
msg.visual_current_temperature_step = traits.get_visual_current_temperature_step();
|
||||||
msg.visual_min_humidity = traits.get_visual_min_humidity();
|
msg.visual_min_humidity = traits.get_visual_min_humidity();
|
||||||
msg.visual_max_humidity = traits.get_visual_max_humidity();
|
msg.visual_max_humidity = traits.get_visual_max_humidity();
|
||||||
msg.legacy_supports_away = traits.supports_preset(climate::CLIMATE_PRESET_AWAY);
|
|
||||||
msg.supports_action = traits.get_supports_action();
|
msg.supports_action = traits.get_supports_action();
|
||||||
for (auto fan_mode : traits.get_supported_fan_modes())
|
for (auto fan_mode : traits.get_supported_fan_modes())
|
||||||
msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
|
msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
|
||||||
@ -1113,21 +1091,6 @@ void APIConnection::subscribe_bluetooth_le_advertisements(const SubscribeBluetoo
|
|||||||
void APIConnection::unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
|
void APIConnection::unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||||
bluetooth_proxy::global_bluetooth_proxy->unsubscribe_api_connection(this);
|
bluetooth_proxy::global_bluetooth_proxy->unsubscribe_api_connection(this);
|
||||||
}
|
}
|
||||||
bool APIConnection::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg) {
|
|
||||||
if (this->client_api_version_major_ < 1 || this->client_api_version_minor_ < 7) {
|
|
||||||
BluetoothLEAdvertisementResponse resp = msg;
|
|
||||||
for (auto &service : resp.service_data) {
|
|
||||||
service.legacy_data.assign(service.data.begin(), service.data.end());
|
|
||||||
service.data.clear();
|
|
||||||
}
|
|
||||||
for (auto &manufacturer_data : resp.manufacturer_data) {
|
|
||||||
manufacturer_data.legacy_data.assign(manufacturer_data.data.begin(), manufacturer_data.data.end());
|
|
||||||
manufacturer_data.data.clear();
|
|
||||||
}
|
|
||||||
return this->send_message(resp, BluetoothLEAdvertisementResponse::MESSAGE_TYPE);
|
|
||||||
}
|
|
||||||
return this->send_message(msg, BluetoothLEAdvertisementResponse::MESSAGE_TYPE);
|
|
||||||
}
|
|
||||||
void APIConnection::bluetooth_device_request(const BluetoothDeviceRequest &msg) {
|
void APIConnection::bluetooth_device_request(const BluetoothDeviceRequest &msg) {
|
||||||
bluetooth_proxy::global_bluetooth_proxy->bluetooth_device_request(msg);
|
bluetooth_proxy::global_bluetooth_proxy->bluetooth_device_request(msg);
|
||||||
}
|
}
|
||||||
@ -1499,12 +1462,10 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
|
|||||||
resp.webserver_port = USE_WEBSERVER_PORT;
|
resp.webserver_port = USE_WEBSERVER_PORT;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
resp.legacy_bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->get_legacy_version();
|
|
||||||
resp.bluetooth_proxy_feature_flags = bluetooth_proxy::global_bluetooth_proxy->get_feature_flags();
|
resp.bluetooth_proxy_feature_flags = bluetooth_proxy::global_bluetooth_proxy->get_feature_flags();
|
||||||
resp.bluetooth_mac_address = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_mac_address_pretty();
|
resp.bluetooth_mac_address = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_mac_address_pretty();
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
resp.legacy_voice_assistant_version = voice_assistant::global_voice_assistant->get_legacy_version();
|
|
||||||
resp.voice_assistant_feature_flags = voice_assistant::global_voice_assistant->get_feature_flags();
|
resp.voice_assistant_feature_flags = voice_assistant::global_voice_assistant->get_feature_flags();
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
|
@ -116,7 +116,6 @@ class APIConnection : public APIServerConnection {
|
|||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||||
void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||||
bool send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &msg);
|
|
||||||
|
|
||||||
void bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
|
void bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
|
||||||
void bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) override;
|
void bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) override;
|
||||||
|
@ -94,17 +94,11 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
|
|||||||
#ifdef USE_WEBSERVER
|
#ifdef USE_WEBSERVER
|
||||||
buffer.encode_uint32(10, this->webserver_port);
|
buffer.encode_uint32(10, this->webserver_port);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
|
||||||
buffer.encode_uint32(11, this->legacy_bluetooth_proxy_version);
|
|
||||||
#endif
|
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
buffer.encode_uint32(15, this->bluetooth_proxy_feature_flags);
|
buffer.encode_uint32(15, this->bluetooth_proxy_feature_flags);
|
||||||
#endif
|
#endif
|
||||||
buffer.encode_string(12, this->manufacturer);
|
buffer.encode_string(12, this->manufacturer);
|
||||||
buffer.encode_string(13, this->friendly_name);
|
buffer.encode_string(13, this->friendly_name);
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
|
||||||
buffer.encode_uint32(14, this->legacy_voice_assistant_version);
|
|
||||||
#endif
|
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
buffer.encode_uint32(17, this->voice_assistant_feature_flags);
|
buffer.encode_uint32(17, this->voice_assistant_feature_flags);
|
||||||
#endif
|
#endif
|
||||||
@ -150,17 +144,11 @@ void DeviceInfoResponse::calculate_size(uint32_t &total_size) const {
|
|||||||
#ifdef USE_WEBSERVER
|
#ifdef USE_WEBSERVER
|
||||||
ProtoSize::add_uint32_field(total_size, 1, this->webserver_port);
|
ProtoSize::add_uint32_field(total_size, 1, this->webserver_port);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
|
||||||
ProtoSize::add_uint32_field(total_size, 1, this->legacy_bluetooth_proxy_version);
|
|
||||||
#endif
|
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
ProtoSize::add_uint32_field(total_size, 1, this->bluetooth_proxy_feature_flags);
|
ProtoSize::add_uint32_field(total_size, 1, this->bluetooth_proxy_feature_flags);
|
||||||
#endif
|
#endif
|
||||||
ProtoSize::add_string_field(total_size, 1, this->manufacturer);
|
ProtoSize::add_string_field(total_size, 1, this->manufacturer);
|
||||||
ProtoSize::add_string_field(total_size, 1, this->friendly_name);
|
ProtoSize::add_string_field(total_size, 1, this->friendly_name);
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
|
||||||
ProtoSize::add_uint32_field(total_size, 1, this->legacy_voice_assistant_version);
|
|
||||||
#endif
|
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
ProtoSize::add_uint32_field(total_size, 2, this->voice_assistant_feature_flags);
|
ProtoSize::add_uint32_field(total_size, 2, this->voice_assistant_feature_flags);
|
||||||
#endif
|
#endif
|
||||||
@ -270,7 +258,6 @@ void ListEntitiesCoverResponse::calculate_size(uint32_t &total_size) const {
|
|||||||
}
|
}
|
||||||
void CoverStateResponse::encode(ProtoWriteBuffer buffer) const {
|
void CoverStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_fixed32(1, this->key);
|
buffer.encode_fixed32(1, this->key);
|
||||||
buffer.encode_uint32(2, static_cast<uint32_t>(this->legacy_state));
|
|
||||||
buffer.encode_float(3, this->position);
|
buffer.encode_float(3, this->position);
|
||||||
buffer.encode_float(4, this->tilt);
|
buffer.encode_float(4, this->tilt);
|
||||||
buffer.encode_uint32(5, static_cast<uint32_t>(this->current_operation));
|
buffer.encode_uint32(5, static_cast<uint32_t>(this->current_operation));
|
||||||
@ -280,7 +267,6 @@ void CoverStateResponse::encode(ProtoWriteBuffer buffer) const {
|
|||||||
}
|
}
|
||||||
void CoverStateResponse::calculate_size(uint32_t &total_size) const {
|
void CoverStateResponse::calculate_size(uint32_t &total_size) const {
|
||||||
ProtoSize::add_fixed32_field(total_size, 1, this->key);
|
ProtoSize::add_fixed32_field(total_size, 1, this->key);
|
||||||
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->legacy_state));
|
|
||||||
ProtoSize::add_float_field(total_size, 1, this->position);
|
ProtoSize::add_float_field(total_size, 1, this->position);
|
||||||
ProtoSize::add_float_field(total_size, 1, this->tilt);
|
ProtoSize::add_float_field(total_size, 1, this->tilt);
|
||||||
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->current_operation));
|
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->current_operation));
|
||||||
@ -290,12 +276,6 @@ void CoverStateResponse::calculate_size(uint32_t &total_size) const {
|
|||||||
}
|
}
|
||||||
bool CoverCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool CoverCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case 2:
|
|
||||||
this->has_legacy_command = value.as_bool();
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
this->legacy_command = static_cast<enums::LegacyCoverCommand>(value.as_uint32());
|
|
||||||
break;
|
|
||||||
case 4:
|
case 4:
|
||||||
this->has_position = value.as_bool();
|
this->has_position = value.as_bool();
|
||||||
break;
|
break;
|
||||||
@ -379,7 +359,6 @@ void FanStateResponse::encode(ProtoWriteBuffer buffer) const {
|
|||||||
buffer.encode_fixed32(1, this->key);
|
buffer.encode_fixed32(1, this->key);
|
||||||
buffer.encode_bool(2, this->state);
|
buffer.encode_bool(2, this->state);
|
||||||
buffer.encode_bool(3, this->oscillating);
|
buffer.encode_bool(3, this->oscillating);
|
||||||
buffer.encode_uint32(4, static_cast<uint32_t>(this->speed));
|
|
||||||
buffer.encode_uint32(5, static_cast<uint32_t>(this->direction));
|
buffer.encode_uint32(5, static_cast<uint32_t>(this->direction));
|
||||||
buffer.encode_int32(6, this->speed_level);
|
buffer.encode_int32(6, this->speed_level);
|
||||||
buffer.encode_string(7, this->preset_mode);
|
buffer.encode_string(7, this->preset_mode);
|
||||||
@ -391,7 +370,6 @@ void FanStateResponse::calculate_size(uint32_t &total_size) const {
|
|||||||
ProtoSize::add_fixed32_field(total_size, 1, this->key);
|
ProtoSize::add_fixed32_field(total_size, 1, this->key);
|
||||||
ProtoSize::add_bool_field(total_size, 1, this->state);
|
ProtoSize::add_bool_field(total_size, 1, this->state);
|
||||||
ProtoSize::add_bool_field(total_size, 1, this->oscillating);
|
ProtoSize::add_bool_field(total_size, 1, this->oscillating);
|
||||||
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->speed));
|
|
||||||
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->direction));
|
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->direction));
|
||||||
ProtoSize::add_int32_field(total_size, 1, this->speed_level);
|
ProtoSize::add_int32_field(total_size, 1, this->speed_level);
|
||||||
ProtoSize::add_string_field(total_size, 1, this->preset_mode);
|
ProtoSize::add_string_field(total_size, 1, this->preset_mode);
|
||||||
@ -407,12 +385,6 @@ bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
|||||||
case 3:
|
case 3:
|
||||||
this->state = value.as_bool();
|
this->state = value.as_bool();
|
||||||
break;
|
break;
|
||||||
case 4:
|
|
||||||
this->has_speed = value.as_bool();
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
this->speed = static_cast<enums::FanSpeed>(value.as_uint32());
|
|
||||||
break;
|
|
||||||
case 6:
|
case 6:
|
||||||
this->has_oscillating = value.as_bool();
|
this->has_oscillating = value.as_bool();
|
||||||
break;
|
break;
|
||||||
@ -473,10 +445,6 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const {
|
|||||||
for (auto &it : this->supported_color_modes) {
|
for (auto &it : this->supported_color_modes) {
|
||||||
buffer.encode_uint32(12, static_cast<uint32_t>(it), true);
|
buffer.encode_uint32(12, static_cast<uint32_t>(it), true);
|
||||||
}
|
}
|
||||||
buffer.encode_bool(5, this->legacy_supports_brightness);
|
|
||||||
buffer.encode_bool(6, this->legacy_supports_rgb);
|
|
||||||
buffer.encode_bool(7, this->legacy_supports_white_value);
|
|
||||||
buffer.encode_bool(8, this->legacy_supports_color_temperature);
|
|
||||||
buffer.encode_float(9, this->min_mireds);
|
buffer.encode_float(9, this->min_mireds);
|
||||||
buffer.encode_float(10, this->max_mireds);
|
buffer.encode_float(10, this->max_mireds);
|
||||||
for (auto &it : this->effects) {
|
for (auto &it : this->effects) {
|
||||||
@ -500,10 +468,6 @@ void ListEntitiesLightResponse::calculate_size(uint32_t &total_size) const {
|
|||||||
ProtoSize::add_enum_field_repeated(total_size, 1, static_cast<uint32_t>(it));
|
ProtoSize::add_enum_field_repeated(total_size, 1, static_cast<uint32_t>(it));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ProtoSize::add_bool_field(total_size, 1, this->legacy_supports_brightness);
|
|
||||||
ProtoSize::add_bool_field(total_size, 1, this->legacy_supports_rgb);
|
|
||||||
ProtoSize::add_bool_field(total_size, 1, this->legacy_supports_white_value);
|
|
||||||
ProtoSize::add_bool_field(total_size, 1, this->legacy_supports_color_temperature);
|
|
||||||
ProtoSize::add_float_field(total_size, 1, this->min_mireds);
|
ProtoSize::add_float_field(total_size, 1, this->min_mireds);
|
||||||
ProtoSize::add_float_field(total_size, 1, this->max_mireds);
|
ProtoSize::add_float_field(total_size, 1, this->max_mireds);
|
||||||
if (!this->effects.empty()) {
|
if (!this->effects.empty()) {
|
||||||
@ -677,7 +641,6 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
|
|||||||
buffer.encode_bool(8, this->force_update);
|
buffer.encode_bool(8, this->force_update);
|
||||||
buffer.encode_string(9, this->device_class);
|
buffer.encode_string(9, this->device_class);
|
||||||
buffer.encode_uint32(10, static_cast<uint32_t>(this->state_class));
|
buffer.encode_uint32(10, static_cast<uint32_t>(this->state_class));
|
||||||
buffer.encode_uint32(11, static_cast<uint32_t>(this->legacy_last_reset_type));
|
|
||||||
buffer.encode_bool(12, this->disabled_by_default);
|
buffer.encode_bool(12, this->disabled_by_default);
|
||||||
buffer.encode_uint32(13, static_cast<uint32_t>(this->entity_category));
|
buffer.encode_uint32(13, static_cast<uint32_t>(this->entity_category));
|
||||||
#ifdef USE_DEVICES
|
#ifdef USE_DEVICES
|
||||||
@ -696,7 +659,6 @@ void ListEntitiesSensorResponse::calculate_size(uint32_t &total_size) const {
|
|||||||
ProtoSize::add_bool_field(total_size, 1, this->force_update);
|
ProtoSize::add_bool_field(total_size, 1, this->force_update);
|
||||||
ProtoSize::add_string_field(total_size, 1, this->device_class);
|
ProtoSize::add_string_field(total_size, 1, this->device_class);
|
||||||
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->state_class));
|
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->state_class));
|
||||||
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->legacy_last_reset_type));
|
|
||||||
ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default);
|
ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default);
|
||||||
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category));
|
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category));
|
||||||
#ifdef USE_DEVICES
|
#ifdef USE_DEVICES
|
||||||
@ -1105,7 +1067,6 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
|
|||||||
buffer.encode_float(8, this->visual_min_temperature);
|
buffer.encode_float(8, this->visual_min_temperature);
|
||||||
buffer.encode_float(9, this->visual_max_temperature);
|
buffer.encode_float(9, this->visual_max_temperature);
|
||||||
buffer.encode_float(10, this->visual_target_temperature_step);
|
buffer.encode_float(10, this->visual_target_temperature_step);
|
||||||
buffer.encode_bool(11, this->legacy_supports_away);
|
|
||||||
buffer.encode_bool(12, this->supports_action);
|
buffer.encode_bool(12, this->supports_action);
|
||||||
for (auto &it : this->supported_fan_modes) {
|
for (auto &it : this->supported_fan_modes) {
|
||||||
buffer.encode_uint32(13, static_cast<uint32_t>(it), true);
|
buffer.encode_uint32(13, static_cast<uint32_t>(it), true);
|
||||||
@ -1150,7 +1111,6 @@ void ListEntitiesClimateResponse::calculate_size(uint32_t &total_size) const {
|
|||||||
ProtoSize::add_float_field(total_size, 1, this->visual_min_temperature);
|
ProtoSize::add_float_field(total_size, 1, this->visual_min_temperature);
|
||||||
ProtoSize::add_float_field(total_size, 1, this->visual_max_temperature);
|
ProtoSize::add_float_field(total_size, 1, this->visual_max_temperature);
|
||||||
ProtoSize::add_float_field(total_size, 1, this->visual_target_temperature_step);
|
ProtoSize::add_float_field(total_size, 1, this->visual_target_temperature_step);
|
||||||
ProtoSize::add_bool_field(total_size, 1, this->legacy_supports_away);
|
|
||||||
ProtoSize::add_bool_field(total_size, 1, this->supports_action);
|
ProtoSize::add_bool_field(total_size, 1, this->supports_action);
|
||||||
if (!this->supported_fan_modes.empty()) {
|
if (!this->supported_fan_modes.empty()) {
|
||||||
for (const auto &it : this->supported_fan_modes) {
|
for (const auto &it : this->supported_fan_modes) {
|
||||||
@ -1198,7 +1158,6 @@ void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
|
|||||||
buffer.encode_float(4, this->target_temperature);
|
buffer.encode_float(4, this->target_temperature);
|
||||||
buffer.encode_float(5, this->target_temperature_low);
|
buffer.encode_float(5, this->target_temperature_low);
|
||||||
buffer.encode_float(6, this->target_temperature_high);
|
buffer.encode_float(6, this->target_temperature_high);
|
||||||
buffer.encode_bool(7, this->unused_legacy_away);
|
|
||||||
buffer.encode_uint32(8, static_cast<uint32_t>(this->action));
|
buffer.encode_uint32(8, static_cast<uint32_t>(this->action));
|
||||||
buffer.encode_uint32(9, static_cast<uint32_t>(this->fan_mode));
|
buffer.encode_uint32(9, static_cast<uint32_t>(this->fan_mode));
|
||||||
buffer.encode_uint32(10, static_cast<uint32_t>(this->swing_mode));
|
buffer.encode_uint32(10, static_cast<uint32_t>(this->swing_mode));
|
||||||
@ -1218,7 +1177,6 @@ void ClimateStateResponse::calculate_size(uint32_t &total_size) const {
|
|||||||
ProtoSize::add_float_field(total_size, 1, this->target_temperature);
|
ProtoSize::add_float_field(total_size, 1, this->target_temperature);
|
||||||
ProtoSize::add_float_field(total_size, 1, this->target_temperature_low);
|
ProtoSize::add_float_field(total_size, 1, this->target_temperature_low);
|
||||||
ProtoSize::add_float_field(total_size, 1, this->target_temperature_high);
|
ProtoSize::add_float_field(total_size, 1, this->target_temperature_high);
|
||||||
ProtoSize::add_bool_field(total_size, 1, this->unused_legacy_away);
|
|
||||||
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->action));
|
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->action));
|
||||||
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->fan_mode));
|
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->fan_mode));
|
||||||
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->swing_mode));
|
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->swing_mode));
|
||||||
@ -1248,12 +1206,6 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
|
|||||||
case 8:
|
case 8:
|
||||||
this->has_target_temperature_high = value.as_bool();
|
this->has_target_temperature_high = value.as_bool();
|
||||||
break;
|
break;
|
||||||
case 10:
|
|
||||||
this->unused_has_legacy_away = value.as_bool();
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
this->unused_legacy_away = value.as_bool();
|
|
||||||
break;
|
|
||||||
case 12:
|
case 12:
|
||||||
this->has_fan_mode = value.as_bool();
|
this->has_fan_mode = value.as_bool();
|
||||||
break;
|
break;
|
||||||
@ -1869,50 +1821,6 @@ bool SubscribeBluetoothLEAdvertisementsRequest::decode_varint(uint32_t field_id,
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void BluetoothServiceData::encode(ProtoWriteBuffer buffer) const {
|
|
||||||
buffer.encode_string(1, this->uuid);
|
|
||||||
for (auto &it : this->legacy_data) {
|
|
||||||
buffer.encode_uint32(2, it, true);
|
|
||||||
}
|
|
||||||
buffer.encode_bytes(3, reinterpret_cast<const uint8_t *>(this->data.data()), this->data.size());
|
|
||||||
}
|
|
||||||
void BluetoothServiceData::calculate_size(uint32_t &total_size) const {
|
|
||||||
ProtoSize::add_string_field(total_size, 1, this->uuid);
|
|
||||||
if (!this->legacy_data.empty()) {
|
|
||||||
for (const auto &it : this->legacy_data) {
|
|
||||||
ProtoSize::add_uint32_field_repeated(total_size, 1, it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ProtoSize::add_string_field(total_size, 1, this->data);
|
|
||||||
}
|
|
||||||
void BluetoothLEAdvertisementResponse::encode(ProtoWriteBuffer buffer) const {
|
|
||||||
buffer.encode_uint64(1, this->address);
|
|
||||||
buffer.encode_bytes(2, reinterpret_cast<const uint8_t *>(this->name.data()), this->name.size());
|
|
||||||
buffer.encode_sint32(3, this->rssi);
|
|
||||||
for (auto &it : this->service_uuids) {
|
|
||||||
buffer.encode_string(4, it, true);
|
|
||||||
}
|
|
||||||
for (auto &it : this->service_data) {
|
|
||||||
buffer.encode_message(5, it, true);
|
|
||||||
}
|
|
||||||
for (auto &it : this->manufacturer_data) {
|
|
||||||
buffer.encode_message(6, it, true);
|
|
||||||
}
|
|
||||||
buffer.encode_uint32(7, this->address_type);
|
|
||||||
}
|
|
||||||
void BluetoothLEAdvertisementResponse::calculate_size(uint32_t &total_size) const {
|
|
||||||
ProtoSize::add_uint64_field(total_size, 1, this->address);
|
|
||||||
ProtoSize::add_string_field(total_size, 1, this->name);
|
|
||||||
ProtoSize::add_sint32_field(total_size, 1, this->rssi);
|
|
||||||
if (!this->service_uuids.empty()) {
|
|
||||||
for (const auto &it : this->service_uuids) {
|
|
||||||
ProtoSize::add_string_field_repeated(total_size, 1, it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ProtoSize::add_repeated_message(total_size, 1, this->service_data);
|
|
||||||
ProtoSize::add_repeated_message(total_size, 1, this->manufacturer_data);
|
|
||||||
ProtoSize::add_uint32_field(total_size, 1, this->address_type);
|
|
||||||
}
|
|
||||||
void BluetoothLERawAdvertisement::encode(ProtoWriteBuffer buffer) const {
|
void BluetoothLERawAdvertisement::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_uint64(1, this->address);
|
buffer.encode_uint64(1, this->address);
|
||||||
buffer.encode_sint32(2, this->rssi);
|
buffer.encode_sint32(2, this->rssi);
|
||||||
|
@ -17,27 +17,13 @@ enum EntityCategory : uint32_t {
|
|||||||
ENTITY_CATEGORY_DIAGNOSTIC = 2,
|
ENTITY_CATEGORY_DIAGNOSTIC = 2,
|
||||||
};
|
};
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
enum LegacyCoverState : uint32_t {
|
|
||||||
LEGACY_COVER_STATE_OPEN = 0,
|
|
||||||
LEGACY_COVER_STATE_CLOSED = 1,
|
|
||||||
};
|
|
||||||
enum CoverOperation : uint32_t {
|
enum CoverOperation : uint32_t {
|
||||||
COVER_OPERATION_IDLE = 0,
|
COVER_OPERATION_IDLE = 0,
|
||||||
COVER_OPERATION_IS_OPENING = 1,
|
COVER_OPERATION_IS_OPENING = 1,
|
||||||
COVER_OPERATION_IS_CLOSING = 2,
|
COVER_OPERATION_IS_CLOSING = 2,
|
||||||
};
|
};
|
||||||
enum LegacyCoverCommand : uint32_t {
|
|
||||||
LEGACY_COVER_COMMAND_OPEN = 0,
|
|
||||||
LEGACY_COVER_COMMAND_CLOSE = 1,
|
|
||||||
LEGACY_COVER_COMMAND_STOP = 2,
|
|
||||||
};
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
enum FanSpeed : uint32_t {
|
|
||||||
FAN_SPEED_LOW = 0,
|
|
||||||
FAN_SPEED_MEDIUM = 1,
|
|
||||||
FAN_SPEED_HIGH = 2,
|
|
||||||
};
|
|
||||||
enum FanDirection : uint32_t {
|
enum FanDirection : uint32_t {
|
||||||
FAN_DIRECTION_FORWARD = 0,
|
FAN_DIRECTION_FORWARD = 0,
|
||||||
FAN_DIRECTION_REVERSE = 1,
|
FAN_DIRECTION_REVERSE = 1,
|
||||||
@ -65,11 +51,6 @@ enum SensorStateClass : uint32_t {
|
|||||||
STATE_CLASS_TOTAL_INCREASING = 2,
|
STATE_CLASS_TOTAL_INCREASING = 2,
|
||||||
STATE_CLASS_TOTAL = 3,
|
STATE_CLASS_TOTAL = 3,
|
||||||
};
|
};
|
||||||
enum SensorLastResetType : uint32_t {
|
|
||||||
LAST_RESET_NONE = 0,
|
|
||||||
LAST_RESET_NEVER = 1,
|
|
||||||
LAST_RESET_AUTO = 2,
|
|
||||||
};
|
|
||||||
#endif
|
#endif
|
||||||
enum LogLevel : uint32_t {
|
enum LogLevel : uint32_t {
|
||||||
LOG_LEVEL_NONE = 0,
|
LOG_LEVEL_NONE = 0,
|
||||||
@ -477,7 +458,7 @@ class DeviceInfo : public ProtoMessage {
|
|||||||
class DeviceInfoResponse : public ProtoMessage {
|
class DeviceInfoResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 10;
|
static constexpr uint8_t MESSAGE_TYPE = 10;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 219;
|
static constexpr uint8_t ESTIMATED_SIZE = 211;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "device_info_response"; }
|
const char *message_name() const override { return "device_info_response"; }
|
||||||
#endif
|
#endif
|
||||||
@ -499,17 +480,11 @@ class DeviceInfoResponse : public ProtoMessage {
|
|||||||
#ifdef USE_WEBSERVER
|
#ifdef USE_WEBSERVER
|
||||||
uint32_t webserver_port{0};
|
uint32_t webserver_port{0};
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
|
||||||
uint32_t legacy_bluetooth_proxy_version{0};
|
|
||||||
#endif
|
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
uint32_t bluetooth_proxy_feature_flags{0};
|
uint32_t bluetooth_proxy_feature_flags{0};
|
||||||
#endif
|
#endif
|
||||||
std::string manufacturer{};
|
std::string manufacturer{};
|
||||||
std::string friendly_name{};
|
std::string friendly_name{};
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
|
||||||
uint32_t legacy_voice_assistant_version{0};
|
|
||||||
#endif
|
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
uint32_t voice_assistant_feature_flags{0};
|
uint32_t voice_assistant_feature_flags{0};
|
||||||
#endif
|
#endif
|
||||||
@ -638,11 +613,10 @@ class ListEntitiesCoverResponse : public InfoResponseProtoMessage {
|
|||||||
class CoverStateResponse : public StateResponseProtoMessage {
|
class CoverStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 22;
|
static constexpr uint8_t MESSAGE_TYPE = 22;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 23;
|
static constexpr uint8_t ESTIMATED_SIZE = 21;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "cover_state_response"; }
|
const char *message_name() const override { return "cover_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
enums::LegacyCoverState legacy_state{};
|
|
||||||
float position{0.0f};
|
float position{0.0f};
|
||||||
float tilt{0.0f};
|
float tilt{0.0f};
|
||||||
enums::CoverOperation current_operation{};
|
enums::CoverOperation current_operation{};
|
||||||
@ -657,12 +631,10 @@ class CoverStateResponse : public StateResponseProtoMessage {
|
|||||||
class CoverCommandRequest : public CommandProtoMessage {
|
class CoverCommandRequest : public CommandProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 30;
|
static constexpr uint8_t MESSAGE_TYPE = 30;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 29;
|
static constexpr uint8_t ESTIMATED_SIZE = 25;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "cover_command_request"; }
|
const char *message_name() const override { return "cover_command_request"; }
|
||||||
#endif
|
#endif
|
||||||
bool has_legacy_command{false};
|
|
||||||
enums::LegacyCoverCommand legacy_command{};
|
|
||||||
bool has_position{false};
|
bool has_position{false};
|
||||||
float position{0.0f};
|
float position{0.0f};
|
||||||
bool has_tilt{false};
|
bool has_tilt{false};
|
||||||
@ -701,13 +673,12 @@ class ListEntitiesFanResponse : public InfoResponseProtoMessage {
|
|||||||
class FanStateResponse : public StateResponseProtoMessage {
|
class FanStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 23;
|
static constexpr uint8_t MESSAGE_TYPE = 23;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 30;
|
static constexpr uint8_t ESTIMATED_SIZE = 28;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "fan_state_response"; }
|
const char *message_name() const override { return "fan_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
bool state{false};
|
bool state{false};
|
||||||
bool oscillating{false};
|
bool oscillating{false};
|
||||||
enums::FanSpeed speed{};
|
|
||||||
enums::FanDirection direction{};
|
enums::FanDirection direction{};
|
||||||
int32_t speed_level{0};
|
int32_t speed_level{0};
|
||||||
std::string preset_mode{};
|
std::string preset_mode{};
|
||||||
@ -722,14 +693,12 @@ class FanStateResponse : public StateResponseProtoMessage {
|
|||||||
class FanCommandRequest : public CommandProtoMessage {
|
class FanCommandRequest : public CommandProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 31;
|
static constexpr uint8_t MESSAGE_TYPE = 31;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 42;
|
static constexpr uint8_t ESTIMATED_SIZE = 38;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "fan_command_request"; }
|
const char *message_name() const override { return "fan_command_request"; }
|
||||||
#endif
|
#endif
|
||||||
bool has_state{false};
|
bool has_state{false};
|
||||||
bool state{false};
|
bool state{false};
|
||||||
bool has_speed{false};
|
|
||||||
enums::FanSpeed speed{};
|
|
||||||
bool has_oscillating{false};
|
bool has_oscillating{false};
|
||||||
bool oscillating{false};
|
bool oscillating{false};
|
||||||
bool has_direction{false};
|
bool has_direction{false};
|
||||||
@ -752,15 +721,11 @@ class FanCommandRequest : public CommandProtoMessage {
|
|||||||
class ListEntitiesLightResponse : public InfoResponseProtoMessage {
|
class ListEntitiesLightResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 15;
|
static constexpr uint8_t MESSAGE_TYPE = 15;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 81;
|
static constexpr uint8_t ESTIMATED_SIZE = 73;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "list_entities_light_response"; }
|
const char *message_name() const override { return "list_entities_light_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::vector<enums::ColorMode> supported_color_modes{};
|
std::vector<enums::ColorMode> supported_color_modes{};
|
||||||
bool legacy_supports_brightness{false};
|
|
||||||
bool legacy_supports_rgb{false};
|
|
||||||
bool legacy_supports_white_value{false};
|
|
||||||
bool legacy_supports_color_temperature{false};
|
|
||||||
float min_mireds{0.0f};
|
float min_mireds{0.0f};
|
||||||
float max_mireds{0.0f};
|
float max_mireds{0.0f};
|
||||||
std::vector<std::string> effects{};
|
std::vector<std::string> effects{};
|
||||||
@ -846,7 +811,7 @@ class LightCommandRequest : public CommandProtoMessage {
|
|||||||
class ListEntitiesSensorResponse : public InfoResponseProtoMessage {
|
class ListEntitiesSensorResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 16;
|
static constexpr uint8_t MESSAGE_TYPE = 16;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 68;
|
static constexpr uint8_t ESTIMATED_SIZE = 66;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "list_entities_sensor_response"; }
|
const char *message_name() const override { return "list_entities_sensor_response"; }
|
||||||
#endif
|
#endif
|
||||||
@ -855,7 +820,6 @@ class ListEntitiesSensorResponse : public InfoResponseProtoMessage {
|
|||||||
bool force_update{false};
|
bool force_update{false};
|
||||||
std::string device_class{};
|
std::string device_class{};
|
||||||
enums::SensorStateClass state_class{};
|
enums::SensorStateClass state_class{};
|
||||||
enums::SensorLastResetType legacy_last_reset_type{};
|
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@ -1281,7 +1245,7 @@ class CameraImageRequest : public ProtoDecodableMessage {
|
|||||||
class ListEntitiesClimateResponse : public InfoResponseProtoMessage {
|
class ListEntitiesClimateResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 46;
|
static constexpr uint8_t MESSAGE_TYPE = 46;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 147;
|
static constexpr uint8_t ESTIMATED_SIZE = 145;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "list_entities_climate_response"; }
|
const char *message_name() const override { return "list_entities_climate_response"; }
|
||||||
#endif
|
#endif
|
||||||
@ -1291,7 +1255,6 @@ class ListEntitiesClimateResponse : public InfoResponseProtoMessage {
|
|||||||
float visual_min_temperature{0.0f};
|
float visual_min_temperature{0.0f};
|
||||||
float visual_max_temperature{0.0f};
|
float visual_max_temperature{0.0f};
|
||||||
float visual_target_temperature_step{0.0f};
|
float visual_target_temperature_step{0.0f};
|
||||||
bool legacy_supports_away{false};
|
|
||||||
bool supports_action{false};
|
bool supports_action{false};
|
||||||
std::vector<enums::ClimateFanMode> supported_fan_modes{};
|
std::vector<enums::ClimateFanMode> supported_fan_modes{};
|
||||||
std::vector<enums::ClimateSwingMode> supported_swing_modes{};
|
std::vector<enums::ClimateSwingMode> supported_swing_modes{};
|
||||||
@ -1314,7 +1277,7 @@ class ListEntitiesClimateResponse : public InfoResponseProtoMessage {
|
|||||||
class ClimateStateResponse : public StateResponseProtoMessage {
|
class ClimateStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 47;
|
static constexpr uint8_t MESSAGE_TYPE = 47;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 70;
|
static constexpr uint8_t ESTIMATED_SIZE = 68;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "climate_state_response"; }
|
const char *message_name() const override { return "climate_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
@ -1323,7 +1286,6 @@ class ClimateStateResponse : public StateResponseProtoMessage {
|
|||||||
float target_temperature{0.0f};
|
float target_temperature{0.0f};
|
||||||
float target_temperature_low{0.0f};
|
float target_temperature_low{0.0f};
|
||||||
float target_temperature_high{0.0f};
|
float target_temperature_high{0.0f};
|
||||||
bool unused_legacy_away{false};
|
|
||||||
enums::ClimateAction action{};
|
enums::ClimateAction action{};
|
||||||
enums::ClimateFanMode fan_mode{};
|
enums::ClimateFanMode fan_mode{};
|
||||||
enums::ClimateSwingMode swing_mode{};
|
enums::ClimateSwingMode swing_mode{};
|
||||||
@ -1343,7 +1305,7 @@ class ClimateStateResponse : public StateResponseProtoMessage {
|
|||||||
class ClimateCommandRequest : public CommandProtoMessage {
|
class ClimateCommandRequest : public CommandProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 48;
|
static constexpr uint8_t MESSAGE_TYPE = 48;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 88;
|
static constexpr uint8_t ESTIMATED_SIZE = 84;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "climate_command_request"; }
|
const char *message_name() const override { return "climate_command_request"; }
|
||||||
#endif
|
#endif
|
||||||
@ -1355,8 +1317,6 @@ class ClimateCommandRequest : public CommandProtoMessage {
|
|||||||
float target_temperature_low{0.0f};
|
float target_temperature_low{0.0f};
|
||||||
bool has_target_temperature_high{false};
|
bool has_target_temperature_high{false};
|
||||||
float target_temperature_high{0.0f};
|
float target_temperature_high{0.0f};
|
||||||
bool unused_has_legacy_away{false};
|
|
||||||
bool unused_legacy_away{false};
|
|
||||||
bool has_fan_mode{false};
|
bool has_fan_mode{false};
|
||||||
enums::ClimateFanMode fan_mode{};
|
enums::ClimateFanMode fan_mode{};
|
||||||
bool has_swing_mode{false};
|
bool has_swing_mode{false};
|
||||||
@ -1728,41 +1688,6 @@ class SubscribeBluetoothLEAdvertisementsRequest : public ProtoDecodableMessage {
|
|||||||
protected:
|
protected:
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class BluetoothServiceData : public ProtoMessage {
|
|
||||||
public:
|
|
||||||
std::string uuid{};
|
|
||||||
std::vector<uint32_t> legacy_data{};
|
|
||||||
std::string data{};
|
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
|
||||||
void dump_to(std::string &out) const override;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
protected:
|
|
||||||
};
|
|
||||||
class BluetoothLEAdvertisementResponse : public ProtoMessage {
|
|
||||||
public:
|
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 67;
|
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 107;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
|
||||||
const char *message_name() const override { return "bluetooth_le_advertisement_response"; }
|
|
||||||
#endif
|
|
||||||
uint64_t address{0};
|
|
||||||
std::string name{};
|
|
||||||
int32_t rssi{0};
|
|
||||||
std::vector<std::string> service_uuids{};
|
|
||||||
std::vector<BluetoothServiceData> service_data{};
|
|
||||||
std::vector<BluetoothServiceData> manufacturer_data{};
|
|
||||||
uint32_t address_type{0};
|
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
|
||||||
void dump_to(std::string &out) const override;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
protected:
|
|
||||||
};
|
|
||||||
class BluetoothLERawAdvertisement : public ProtoMessage {
|
class BluetoothLERawAdvertisement : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint64_t address{0};
|
uint64_t address{0};
|
||||||
|
@ -23,16 +23,6 @@ template<> const char *proto_enum_to_string<enums::EntityCategory>(enums::Entity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
template<> const char *proto_enum_to_string<enums::LegacyCoverState>(enums::LegacyCoverState value) {
|
|
||||||
switch (value) {
|
|
||||||
case enums::LEGACY_COVER_STATE_OPEN:
|
|
||||||
return "LEGACY_COVER_STATE_OPEN";
|
|
||||||
case enums::LEGACY_COVER_STATE_CLOSED:
|
|
||||||
return "LEGACY_COVER_STATE_CLOSED";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template<> const char *proto_enum_to_string<enums::CoverOperation>(enums::CoverOperation value) {
|
template<> const char *proto_enum_to_string<enums::CoverOperation>(enums::CoverOperation value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case enums::COVER_OPERATION_IDLE:
|
case enums::COVER_OPERATION_IDLE:
|
||||||
@ -45,32 +35,8 @@ template<> const char *proto_enum_to_string<enums::CoverOperation>(enums::CoverO
|
|||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template<> const char *proto_enum_to_string<enums::LegacyCoverCommand>(enums::LegacyCoverCommand value) {
|
|
||||||
switch (value) {
|
|
||||||
case enums::LEGACY_COVER_COMMAND_OPEN:
|
|
||||||
return "LEGACY_COVER_COMMAND_OPEN";
|
|
||||||
case enums::LEGACY_COVER_COMMAND_CLOSE:
|
|
||||||
return "LEGACY_COVER_COMMAND_CLOSE";
|
|
||||||
case enums::LEGACY_COVER_COMMAND_STOP:
|
|
||||||
return "LEGACY_COVER_COMMAND_STOP";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
template<> const char *proto_enum_to_string<enums::FanSpeed>(enums::FanSpeed value) {
|
|
||||||
switch (value) {
|
|
||||||
case enums::FAN_SPEED_LOW:
|
|
||||||
return "FAN_SPEED_LOW";
|
|
||||||
case enums::FAN_SPEED_MEDIUM:
|
|
||||||
return "FAN_SPEED_MEDIUM";
|
|
||||||
case enums::FAN_SPEED_HIGH:
|
|
||||||
return "FAN_SPEED_HIGH";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
template<> const char *proto_enum_to_string<enums::FanDirection>(enums::FanDirection value) {
|
template<> const char *proto_enum_to_string<enums::FanDirection>(enums::FanDirection value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case enums::FAN_DIRECTION_FORWARD:
|
case enums::FAN_DIRECTION_FORWARD:
|
||||||
@ -127,18 +93,6 @@ template<> const char *proto_enum_to_string<enums::SensorStateClass>(enums::Sens
|
|||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template<> const char *proto_enum_to_string<enums::SensorLastResetType>(enums::SensorLastResetType value) {
|
|
||||||
switch (value) {
|
|
||||||
case enums::LAST_RESET_NONE:
|
|
||||||
return "LAST_RESET_NONE";
|
|
||||||
case enums::LAST_RESET_NEVER:
|
|
||||||
return "LAST_RESET_NEVER";
|
|
||||||
case enums::LAST_RESET_AUTO:
|
|
||||||
return "LAST_RESET_AUTO";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
template<> const char *proto_enum_to_string<enums::LogLevel>(enums::LogLevel value) {
|
template<> const char *proto_enum_to_string<enums::LogLevel>(enums::LogLevel value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
@ -737,13 +691,6 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
|
|||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
#endif
|
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
|
||||||
out.append(" legacy_bluetooth_proxy_version: ");
|
|
||||||
snprintf(buffer, sizeof(buffer), "%" PRIu32, this->legacy_bluetooth_proxy_version);
|
|
||||||
out.append(buffer);
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
out.append(" bluetooth_proxy_feature_flags: ");
|
out.append(" bluetooth_proxy_feature_flags: ");
|
||||||
@ -760,13 +707,6 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
|
|||||||
out.append("'").append(this->friendly_name).append("'");
|
out.append("'").append(this->friendly_name).append("'");
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
|
||||||
out.append(" legacy_voice_assistant_version: ");
|
|
||||||
snprintf(buffer, sizeof(buffer), "%" PRIu32, this->legacy_voice_assistant_version);
|
|
||||||
out.append(buffer);
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
out.append(" voice_assistant_feature_flags: ");
|
out.append(" voice_assistant_feature_flags: ");
|
||||||
snprintf(buffer, sizeof(buffer), "%" PRIu32, this->voice_assistant_feature_flags);
|
snprintf(buffer, sizeof(buffer), "%" PRIu32, this->voice_assistant_feature_flags);
|
||||||
@ -961,10 +901,6 @@ void CoverStateResponse::dump_to(std::string &out) const {
|
|||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" legacy_state: ");
|
|
||||||
out.append(proto_enum_to_string<enums::LegacyCoverState>(this->legacy_state));
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" position: ");
|
out.append(" position: ");
|
||||||
snprintf(buffer, sizeof(buffer), "%g", this->position);
|
snprintf(buffer, sizeof(buffer), "%g", this->position);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
@ -996,14 +932,6 @@ void CoverCommandRequest::dump_to(std::string &out) const {
|
|||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" has_legacy_command: ");
|
|
||||||
out.append(YESNO(this->has_legacy_command));
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" legacy_command: ");
|
|
||||||
out.append(proto_enum_to_string<enums::LegacyCoverCommand>(this->legacy_command));
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" has_position: ");
|
out.append(" has_position: ");
|
||||||
out.append(YESNO(this->has_position));
|
out.append(YESNO(this->has_position));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
@ -1115,10 +1043,6 @@ void FanStateResponse::dump_to(std::string &out) const {
|
|||||||
out.append(YESNO(this->oscillating));
|
out.append(YESNO(this->oscillating));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" speed: ");
|
|
||||||
out.append(proto_enum_to_string<enums::FanSpeed>(this->speed));
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" direction: ");
|
out.append(" direction: ");
|
||||||
out.append(proto_enum_to_string<enums::FanDirection>(this->direction));
|
out.append(proto_enum_to_string<enums::FanDirection>(this->direction));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
@ -1157,14 +1081,6 @@ void FanCommandRequest::dump_to(std::string &out) const {
|
|||||||
out.append(YESNO(this->state));
|
out.append(YESNO(this->state));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" has_speed: ");
|
|
||||||
out.append(YESNO(this->has_speed));
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" speed: ");
|
|
||||||
out.append(proto_enum_to_string<enums::FanSpeed>(this->speed));
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" has_oscillating: ");
|
out.append(" has_oscillating: ");
|
||||||
out.append(YESNO(this->has_oscillating));
|
out.append(YESNO(this->has_oscillating));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
@ -1231,22 +1147,6 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const {
|
|||||||
out.append("\n");
|
out.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
out.append(" legacy_supports_brightness: ");
|
|
||||||
out.append(YESNO(this->legacy_supports_brightness));
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" legacy_supports_rgb: ");
|
|
||||||
out.append(YESNO(this->legacy_supports_rgb));
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" legacy_supports_white_value: ");
|
|
||||||
out.append(YESNO(this->legacy_supports_white_value));
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" legacy_supports_color_temperature: ");
|
|
||||||
out.append(YESNO(this->legacy_supports_color_temperature));
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" min_mireds: ");
|
out.append(" min_mireds: ");
|
||||||
snprintf(buffer, sizeof(buffer), "%g", this->min_mireds);
|
snprintf(buffer, sizeof(buffer), "%g", this->min_mireds);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
@ -1537,10 +1437,6 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
|
|||||||
out.append(proto_enum_to_string<enums::SensorStateClass>(this->state_class));
|
out.append(proto_enum_to_string<enums::SensorStateClass>(this->state_class));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" legacy_last_reset_type: ");
|
|
||||||
out.append(proto_enum_to_string<enums::SensorLastResetType>(this->legacy_last_reset_type));
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" disabled_by_default: ");
|
out.append(" disabled_by_default: ");
|
||||||
out.append(YESNO(this->disabled_by_default));
|
out.append(YESNO(this->disabled_by_default));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
@ -2107,10 +2003,6 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
|||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" legacy_supports_away: ");
|
|
||||||
out.append(YESNO(this->legacy_supports_away));
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" supports_action: ");
|
out.append(" supports_action: ");
|
||||||
out.append(YESNO(this->supports_action));
|
out.append(YESNO(this->supports_action));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
@ -2223,10 +2115,6 @@ void ClimateStateResponse::dump_to(std::string &out) const {
|
|||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" unused_legacy_away: ");
|
|
||||||
out.append(YESNO(this->unused_legacy_away));
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" action: ");
|
out.append(" action: ");
|
||||||
out.append(proto_enum_to_string<enums::ClimateAction>(this->action));
|
out.append(proto_enum_to_string<enums::ClimateAction>(this->action));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
@ -2313,14 +2201,6 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
|
|||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" unused_has_legacy_away: ");
|
|
||||||
out.append(YESNO(this->unused_has_legacy_away));
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" unused_legacy_away: ");
|
|
||||||
out.append(YESNO(this->unused_legacy_away));
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" has_fan_mode: ");
|
out.append(" has_fan_mode: ");
|
||||||
out.append(YESNO(this->has_fan_mode));
|
out.append(YESNO(this->has_fan_mode));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
@ -3053,66 +2933,6 @@ void SubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const
|
|||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
void BluetoothServiceData::dump_to(std::string &out) const {
|
|
||||||
__attribute__((unused)) char buffer[64];
|
|
||||||
out.append("BluetoothServiceData {\n");
|
|
||||||
out.append(" uuid: ");
|
|
||||||
out.append("'").append(this->uuid).append("'");
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
for (const auto &it : this->legacy_data) {
|
|
||||||
out.append(" legacy_data: ");
|
|
||||||
snprintf(buffer, sizeof(buffer), "%" PRIu32, it);
|
|
||||||
out.append(buffer);
|
|
||||||
out.append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
out.append(" data: ");
|
|
||||||
out.append(format_hex_pretty(this->data));
|
|
||||||
out.append("\n");
|
|
||||||
out.append("}");
|
|
||||||
}
|
|
||||||
void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const {
|
|
||||||
__attribute__((unused)) char buffer[64];
|
|
||||||
out.append("BluetoothLEAdvertisementResponse {\n");
|
|
||||||
out.append(" address: ");
|
|
||||||
snprintf(buffer, sizeof(buffer), "%llu", this->address);
|
|
||||||
out.append(buffer);
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" name: ");
|
|
||||||
out.append(format_hex_pretty(this->name));
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
out.append(" rssi: ");
|
|
||||||
snprintf(buffer, sizeof(buffer), "%" PRId32, this->rssi);
|
|
||||||
out.append(buffer);
|
|
||||||
out.append("\n");
|
|
||||||
|
|
||||||
for (const auto &it : this->service_uuids) {
|
|
||||||
out.append(" service_uuids: ");
|
|
||||||
out.append("'").append(it).append("'");
|
|
||||||
out.append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &it : this->service_data) {
|
|
||||||
out.append(" service_data: ");
|
|
||||||
it.dump_to(out);
|
|
||||||
out.append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &it : this->manufacturer_data) {
|
|
||||||
out.append(" manufacturer_data: ");
|
|
||||||
it.dump_to(out);
|
|
||||||
out.append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
out.append(" address_type: ");
|
|
||||||
snprintf(buffer, sizeof(buffer), "%" PRIu32, this->address_type);
|
|
||||||
out.append(buffer);
|
|
||||||
out.append("\n");
|
|
||||||
out.append("}");
|
|
||||||
}
|
|
||||||
void BluetoothLERawAdvertisement::dump_to(std::string &out) const {
|
void BluetoothLERawAdvertisement::dump_to(std::string &out) const {
|
||||||
__attribute__((unused)) char buffer[64];
|
__attribute__((unused)) char buffer[64];
|
||||||
out.append("BluetoothLERawAdvertisement {\n");
|
out.append("BluetoothLERawAdvertisement {\n");
|
||||||
|
@ -140,46 +140,6 @@ void BluetoothProxy::flush_pending_advertisements() {
|
|||||||
this->advertisement_count_ = 0;
|
this->advertisement_count_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_ESP32_BLE_DEVICE
|
|
||||||
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
|
|
||||||
api::BluetoothLEAdvertisementResponse resp;
|
|
||||||
resp.address = device.address_uint64();
|
|
||||||
resp.address_type = device.get_address_type();
|
|
||||||
if (!device.get_name().empty())
|
|
||||||
resp.name = device.get_name();
|
|
||||||
resp.rssi = device.get_rssi();
|
|
||||||
|
|
||||||
// Pre-allocate vectors based on known sizes
|
|
||||||
auto service_uuids = device.get_service_uuids();
|
|
||||||
resp.service_uuids.reserve(service_uuids.size());
|
|
||||||
for (auto &uuid : service_uuids) {
|
|
||||||
resp.service_uuids.emplace_back(uuid.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pre-allocate service data vector
|
|
||||||
auto service_datas = device.get_service_datas();
|
|
||||||
resp.service_data.reserve(service_datas.size());
|
|
||||||
for (auto &data : service_datas) {
|
|
||||||
resp.service_data.emplace_back();
|
|
||||||
auto &service_data = resp.service_data.back();
|
|
||||||
service_data.uuid = data.uuid.to_string();
|
|
||||||
service_data.data.assign(data.data.begin(), data.data.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pre-allocate manufacturer data vector
|
|
||||||
auto manufacturer_datas = device.get_manufacturer_datas();
|
|
||||||
resp.manufacturer_data.reserve(manufacturer_datas.size());
|
|
||||||
for (auto &data : manufacturer_datas) {
|
|
||||||
resp.manufacturer_data.emplace_back();
|
|
||||||
auto &manufacturer_data = resp.manufacturer_data.back();
|
|
||||||
manufacturer_data.uuid = data.uuid.to_string();
|
|
||||||
manufacturer_data.data.assign(data.data.begin(), data.data.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
this->api_connection_->send_message(resp, api::BluetoothLEAdvertisementResponse::MESSAGE_TYPE);
|
|
||||||
}
|
|
||||||
#endif // USE_ESP32_BLE_DEVICE
|
|
||||||
|
|
||||||
void BluetoothProxy::dump_config() {
|
void BluetoothProxy::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "Bluetooth Proxy:");
|
ESP_LOGCONFIG(TAG, "Bluetooth Proxy:");
|
||||||
ESP_LOGCONFIG(TAG,
|
ESP_LOGCONFIG(TAG,
|
||||||
|
@ -131,9 +131,6 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
#ifdef USE_ESP32_BLE_DEVICE
|
|
||||||
void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device);
|
|
||||||
#endif
|
|
||||||
void send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerState state);
|
void send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerState state);
|
||||||
|
|
||||||
BluetoothConnection *get_connection_(uint64_t address, bool reserve);
|
BluetoothConnection *get_connection_(uint64_t address, bool reserve);
|
||||||
|
@ -192,7 +192,7 @@ class WidgetType:
|
|||||||
|
|
||||||
class NumberType(WidgetType):
|
class NumberType(WidgetType):
|
||||||
def get_max(self, config: dict):
|
def get_max(self, config: dict):
|
||||||
return int(config[CONF_MAX_VALUE] or 100)
|
return int(config.get(CONF_MAX_VALUE, 100))
|
||||||
|
|
||||||
def get_min(self, config: dict):
|
def get_min(self, config: dict):
|
||||||
return int(config[CONF_MIN_VALUE] or 0)
|
return int(config.get(CONF_MIN_VALUE, 0))
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
#if defined(USE_ESP32)
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
@ -78,4 +78,4 @@ template<class T, uint8_t SIZE> class EventPool {
|
|||||||
|
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
#endif // defined(USE_ESP32) || defined(USE_LIBRETINY)
|
#endif // defined(USE_ESP32)
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
|
#if defined(USE_ESP32)
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
#if defined(USE_ESP32)
|
|
||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
#elif defined(USE_LIBRETINY)
|
|
||||||
#include <FreeRTOS.h>
|
|
||||||
#include <task.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lock-free queue for single-producer single-consumer scenarios.
|
* Lock-free queue for single-producer single-consumer scenarios.
|
||||||
@ -148,4 +143,4 @@ template<class T, uint8_t SIZE> class NotifyingLockFreeQueue : public LockFreeQu
|
|||||||
|
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
#endif // defined(USE_ESP32) || defined(USE_LIBRETINY)
|
#endif // defined(USE_ESP32)
|
||||||
|
@ -138,7 +138,7 @@ lib_deps =
|
|||||||
WiFi ; wifi,web_server_base,ethernet (Arduino built-in)
|
WiFi ; wifi,web_server_base,ethernet (Arduino built-in)
|
||||||
Update ; ota,web_server_base (Arduino built-in)
|
Update ; ota,web_server_base (Arduino built-in)
|
||||||
${common:arduino.lib_deps}
|
${common:arduino.lib_deps}
|
||||||
ESP32Async/AsyncTCP@3.4.4 ; async_tcp
|
ESP32Async/AsyncTCP@3.4.5 ; async_tcp
|
||||||
NetworkClientSecure ; http_request,nextion (Arduino built-in)
|
NetworkClientSecure ; http_request,nextion (Arduino built-in)
|
||||||
HTTPClient ; http_request,nextion (Arduino built-in)
|
HTTPClient ; http_request,nextion (Arduino built-in)
|
||||||
ESPmDNS ; mdns (Arduino built-in)
|
ESPmDNS ; mdns (Arduino built-in)
|
||||||
|
@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
|
|||||||
esptool==4.9.0
|
esptool==4.9.0
|
||||||
click==8.1.7
|
click==8.1.7
|
||||||
esphome-dashboard==20250514.0
|
esphome-dashboard==20250514.0
|
||||||
aioesphomeapi==36.0.1
|
aioesphomeapi==37.0.0
|
||||||
zeroconf==0.147.0
|
zeroconf==0.147.0
|
||||||
puremagic==1.30
|
puremagic==1.30
|
||||||
ruamel.yaml==0.18.14 # dashboard_import
|
ruamel.yaml==0.18.14 # dashboard_import
|
||||||
|
@ -971,11 +971,11 @@ class RepeatedTypeInfo(TypeInfo):
|
|||||||
|
|
||||||
def build_type_usage_map(
|
def build_type_usage_map(
|
||||||
file_desc: descriptor.FileDescriptorProto,
|
file_desc: descriptor.FileDescriptorProto,
|
||||||
) -> tuple[dict[str, str | None], dict[str, str | None], dict[str, int]]:
|
) -> tuple[dict[str, str | None], dict[str, str | None], dict[str, int], set[str]]:
|
||||||
"""Build mappings for both enums and messages to their ifdefs based on usage.
|
"""Build mappings for both enums and messages to their ifdefs based on usage.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
tuple: (enum_ifdef_map, message_ifdef_map, message_source_map)
|
tuple: (enum_ifdef_map, message_ifdef_map, message_source_map, used_messages)
|
||||||
"""
|
"""
|
||||||
enum_ifdef_map: dict[str, str | None] = {}
|
enum_ifdef_map: dict[str, str | None] = {}
|
||||||
message_ifdef_map: dict[str, str | None] = {}
|
message_ifdef_map: dict[str, str | None] = {}
|
||||||
@ -988,6 +988,7 @@ def build_type_usage_map(
|
|||||||
message_usage: dict[
|
message_usage: dict[
|
||||||
str, set[str]
|
str, set[str]
|
||||||
] = {} # message_name -> set of message names that use it
|
] = {} # message_name -> set of message names that use it
|
||||||
|
used_messages: set[str] = set() # Track which messages are actually used
|
||||||
|
|
||||||
# Build message name to ifdef mapping for quick lookup
|
# Build message name to ifdef mapping for quick lookup
|
||||||
message_to_ifdef: dict[str, str | None] = {
|
message_to_ifdef: dict[str, str | None] = {
|
||||||
@ -996,17 +997,26 @@ def build_type_usage_map(
|
|||||||
|
|
||||||
# Analyze field usage
|
# Analyze field usage
|
||||||
for message in file_desc.message_type:
|
for message in file_desc.message_type:
|
||||||
|
# Skip deprecated messages entirely
|
||||||
|
if message.options.deprecated:
|
||||||
|
continue
|
||||||
|
|
||||||
for field in message.field:
|
for field in message.field:
|
||||||
|
# Skip deprecated fields when tracking enum usage
|
||||||
|
if field.options.deprecated:
|
||||||
|
continue
|
||||||
|
|
||||||
type_name = field.type_name.split(".")[-1] if field.type_name else None
|
type_name = field.type_name.split(".")[-1] if field.type_name else None
|
||||||
if not type_name:
|
if not type_name:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Track enum usage
|
# Track enum usage (only from non-deprecated fields)
|
||||||
if field.type == 14: # TYPE_ENUM
|
if field.type == 14: # TYPE_ENUM
|
||||||
enum_usage.setdefault(type_name, set()).add(message.name)
|
enum_usage.setdefault(type_name, set()).add(message.name)
|
||||||
# Track message usage
|
# Track message usage
|
||||||
elif field.type == 11: # TYPE_MESSAGE
|
elif field.type == 11: # TYPE_MESSAGE
|
||||||
message_usage.setdefault(type_name, set()).add(message.name)
|
message_usage.setdefault(type_name, set()).add(message.name)
|
||||||
|
used_messages.add(type_name)
|
||||||
|
|
||||||
# Helper to get unique ifdef from a set of messages
|
# Helper to get unique ifdef from a set of messages
|
||||||
def get_unique_ifdef(message_names: set[str]) -> str | None:
|
def get_unique_ifdef(message_names: set[str]) -> str | None:
|
||||||
@ -1069,12 +1079,18 @@ def build_type_usage_map(
|
|||||||
# Build message source map
|
# Build message source map
|
||||||
# First pass: Get explicit sources for messages with source option or id
|
# First pass: Get explicit sources for messages with source option or id
|
||||||
for msg in file_desc.message_type:
|
for msg in file_desc.message_type:
|
||||||
|
# Skip deprecated messages
|
||||||
|
if msg.options.deprecated:
|
||||||
|
continue
|
||||||
|
|
||||||
if msg.options.HasExtension(pb.source):
|
if msg.options.HasExtension(pb.source):
|
||||||
# Explicit source option takes precedence
|
# Explicit source option takes precedence
|
||||||
message_source_map[msg.name] = get_opt(msg, pb.source, SOURCE_BOTH)
|
message_source_map[msg.name] = get_opt(msg, pb.source, SOURCE_BOTH)
|
||||||
elif msg.options.HasExtension(pb.id):
|
elif msg.options.HasExtension(pb.id):
|
||||||
# Service messages (with id) default to SOURCE_BOTH
|
# Service messages (with id) default to SOURCE_BOTH
|
||||||
message_source_map[msg.name] = SOURCE_BOTH
|
message_source_map[msg.name] = SOURCE_BOTH
|
||||||
|
# Service messages are always used
|
||||||
|
used_messages.add(msg.name)
|
||||||
|
|
||||||
# Second pass: Determine sources for embedded messages based on their usage
|
# Second pass: Determine sources for embedded messages based on their usage
|
||||||
for msg in file_desc.message_type:
|
for msg in file_desc.message_type:
|
||||||
@ -1103,7 +1119,12 @@ def build_type_usage_map(
|
|||||||
# Not used by any message and no explicit source - default to encode-only
|
# Not used by any message and no explicit source - default to encode-only
|
||||||
message_source_map[msg.name] = SOURCE_SERVER
|
message_source_map[msg.name] = SOURCE_SERVER
|
||||||
|
|
||||||
return enum_ifdef_map, message_ifdef_map, message_source_map
|
return (
|
||||||
|
enum_ifdef_map,
|
||||||
|
message_ifdef_map,
|
||||||
|
message_source_map,
|
||||||
|
used_messages,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def build_enum_type(desc, enum_ifdef_map) -> tuple[str, str, str]:
|
def build_enum_type(desc, enum_ifdef_map) -> tuple[str, str, str]:
|
||||||
@ -1145,6 +1166,10 @@ def calculate_message_estimated_size(desc: descriptor.DescriptorProto) -> int:
|
|||||||
total_size = 0
|
total_size = 0
|
||||||
|
|
||||||
for field in desc.field:
|
for field in desc.field:
|
||||||
|
# Skip deprecated fields
|
||||||
|
if field.options.deprecated:
|
||||||
|
continue
|
||||||
|
|
||||||
ti = create_field_type_info(field)
|
ti = create_field_type_info(field)
|
||||||
|
|
||||||
# Add estimated size for this field
|
# Add estimated size for this field
|
||||||
@ -1213,6 +1238,10 @@ def build_message_type(
|
|||||||
public_content.append("#endif")
|
public_content.append("#endif")
|
||||||
|
|
||||||
for field in desc.field:
|
for field in desc.field:
|
||||||
|
# Skip deprecated fields completely
|
||||||
|
if field.options.deprecated:
|
||||||
|
continue
|
||||||
|
|
||||||
ti = create_field_type_info(field)
|
ti = create_field_type_info(field)
|
||||||
|
|
||||||
# Skip field declarations for fields that are in the base class
|
# Skip field declarations for fields that are in the base class
|
||||||
@ -1459,8 +1488,10 @@ def find_common_fields(
|
|||||||
if not messages:
|
if not messages:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# Start with fields from the first message
|
# Start with fields from the first message (excluding deprecated fields)
|
||||||
first_msg_fields = {field.name: field for field in messages[0].field}
|
first_msg_fields = {
|
||||||
|
field.name: field for field in messages[0].field if not field.options.deprecated
|
||||||
|
}
|
||||||
common_fields = []
|
common_fields = []
|
||||||
|
|
||||||
# Check each field to see if it exists in all messages with same type
|
# Check each field to see if it exists in all messages with same type
|
||||||
@ -1471,6 +1502,9 @@ def find_common_fields(
|
|||||||
for msg in messages[1:]:
|
for msg in messages[1:]:
|
||||||
found = False
|
found = False
|
||||||
for other_field in msg.field:
|
for other_field in msg.field:
|
||||||
|
# Skip deprecated fields
|
||||||
|
if other_field.options.deprecated:
|
||||||
|
continue
|
||||||
if (
|
if (
|
||||||
other_field.name == field_name
|
other_field.name == field_name
|
||||||
and other_field.type == field.type
|
and other_field.type == field.type
|
||||||
@ -1495,6 +1529,7 @@ def build_base_class(
|
|||||||
base_class_name: str,
|
base_class_name: str,
|
||||||
common_fields: list[descriptor.FieldDescriptorProto],
|
common_fields: list[descriptor.FieldDescriptorProto],
|
||||||
messages: list[descriptor.DescriptorProto],
|
messages: list[descriptor.DescriptorProto],
|
||||||
|
message_source_map: dict[str, int],
|
||||||
) -> tuple[str, str, str]:
|
) -> tuple[str, str, str]:
|
||||||
"""Build the base class definition and implementation."""
|
"""Build the base class definition and implementation."""
|
||||||
public_content = []
|
public_content = []
|
||||||
@ -1511,7 +1546,7 @@ def build_base_class(
|
|||||||
|
|
||||||
# Determine if any message using this base class needs decoding
|
# Determine if any message using this base class needs decoding
|
||||||
needs_decode = any(
|
needs_decode = any(
|
||||||
get_opt(msg, pb.source, SOURCE_BOTH) in (SOURCE_BOTH, SOURCE_CLIENT)
|
message_source_map.get(msg.name, SOURCE_BOTH) in (SOURCE_BOTH, SOURCE_CLIENT)
|
||||||
for msg in messages
|
for msg in messages
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1543,6 +1578,7 @@ def build_base_class(
|
|||||||
|
|
||||||
def generate_base_classes(
|
def generate_base_classes(
|
||||||
base_class_groups: dict[str, list[descriptor.DescriptorProto]],
|
base_class_groups: dict[str, list[descriptor.DescriptorProto]],
|
||||||
|
message_source_map: dict[str, int],
|
||||||
) -> tuple[str, str, str]:
|
) -> tuple[str, str, str]:
|
||||||
"""Generate all base classes."""
|
"""Generate all base classes."""
|
||||||
all_headers = []
|
all_headers = []
|
||||||
@ -1556,7 +1592,7 @@ def generate_base_classes(
|
|||||||
if common_fields:
|
if common_fields:
|
||||||
# Generate base class
|
# Generate base class
|
||||||
header, cpp, dump_cpp = build_base_class(
|
header, cpp, dump_cpp = build_base_class(
|
||||||
base_class_name, common_fields, messages
|
base_class_name, common_fields, messages, message_source_map
|
||||||
)
|
)
|
||||||
all_headers.append(header)
|
all_headers.append(header)
|
||||||
all_cpp.append(cpp)
|
all_cpp.append(cpp)
|
||||||
@ -1567,14 +1603,19 @@ def generate_base_classes(
|
|||||||
|
|
||||||
def build_service_message_type(
|
def build_service_message_type(
|
||||||
mt: descriptor.DescriptorProto,
|
mt: descriptor.DescriptorProto,
|
||||||
|
message_source_map: dict[str, int],
|
||||||
) -> tuple[str, str] | None:
|
) -> tuple[str, str] | None:
|
||||||
"""Builds the service message type."""
|
"""Builds the service message type."""
|
||||||
|
# Skip deprecated messages
|
||||||
|
if mt.options.deprecated:
|
||||||
|
return None
|
||||||
|
|
||||||
snake = camel_to_snake(mt.name)
|
snake = camel_to_snake(mt.name)
|
||||||
id_: int | None = get_opt(mt, pb.id)
|
id_: int | None = get_opt(mt, pb.id)
|
||||||
if id_ is None:
|
if id_ is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
source: int = get_opt(mt, pb.source, 0)
|
source: int = message_source_map.get(mt.name, SOURCE_BOTH)
|
||||||
|
|
||||||
ifdef: str | None = get_opt(mt, pb.ifdef)
|
ifdef: str | None = get_opt(mt, pb.ifdef)
|
||||||
log: bool = get_opt(mt, pb.log, True)
|
log: bool = get_opt(mt, pb.log, True)
|
||||||
@ -1670,12 +1711,18 @@ namespace api {
|
|||||||
content += "namespace enums {\n\n"
|
content += "namespace enums {\n\n"
|
||||||
|
|
||||||
# Build dynamic ifdef mappings for both enums and messages
|
# Build dynamic ifdef mappings for both enums and messages
|
||||||
enum_ifdef_map, message_ifdef_map, message_source_map = build_type_usage_map(file)
|
enum_ifdef_map, message_ifdef_map, message_source_map, used_messages = (
|
||||||
|
build_type_usage_map(file)
|
||||||
|
)
|
||||||
|
|
||||||
# Simple grouping of enums by ifdef
|
# Simple grouping of enums by ifdef
|
||||||
current_ifdef = None
|
current_ifdef = None
|
||||||
|
|
||||||
for enum in file.enum_type:
|
for enum in file.enum_type:
|
||||||
|
# Skip deprecated enums
|
||||||
|
if enum.options.deprecated:
|
||||||
|
continue
|
||||||
|
|
||||||
s, c, dc = build_enum_type(enum, enum_ifdef_map)
|
s, c, dc = build_enum_type(enum, enum_ifdef_map)
|
||||||
enum_ifdef = enum_ifdef_map.get(enum.name)
|
enum_ifdef = enum_ifdef_map.get(enum.name)
|
||||||
|
|
||||||
@ -1714,7 +1761,9 @@ namespace api {
|
|||||||
|
|
||||||
# Generate base classes
|
# Generate base classes
|
||||||
if base_class_fields:
|
if base_class_fields:
|
||||||
base_headers, base_cpp, base_dump_cpp = generate_base_classes(base_class_groups)
|
base_headers, base_cpp, base_dump_cpp = generate_base_classes(
|
||||||
|
base_class_groups, message_source_map
|
||||||
|
)
|
||||||
content += base_headers
|
content += base_headers
|
||||||
cpp += base_cpp
|
cpp += base_cpp
|
||||||
dump_cpp += base_dump_cpp
|
dump_cpp += base_dump_cpp
|
||||||
@ -1724,6 +1773,14 @@ namespace api {
|
|||||||
current_ifdef = None
|
current_ifdef = None
|
||||||
|
|
||||||
for m in mt:
|
for m in mt:
|
||||||
|
# Skip deprecated messages
|
||||||
|
if m.options.deprecated:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Skip messages that aren't used (unless they have an ID/service message)
|
||||||
|
if m.name not in used_messages and not m.options.HasExtension(pb.id):
|
||||||
|
continue
|
||||||
|
|
||||||
s, c, dc = build_message_type(m, base_class_fields, message_source_map)
|
s, c, dc = build_message_type(m, base_class_fields, message_source_map)
|
||||||
msg_ifdef = message_ifdef_map.get(m.name)
|
msg_ifdef = message_ifdef_map.get(m.name)
|
||||||
|
|
||||||
@ -1832,7 +1889,7 @@ static const char *const TAG = "api.service";
|
|||||||
cpp += "#endif\n\n"
|
cpp += "#endif\n\n"
|
||||||
|
|
||||||
for mt in file.message_type:
|
for mt in file.message_type:
|
||||||
obj = build_service_message_type(mt)
|
obj = build_service_message_type(mt, message_source_map)
|
||||||
if obj is None:
|
if obj is None:
|
||||||
continue
|
continue
|
||||||
hout, cout = obj
|
hout, cout = obj
|
||||||
|
207
tests/integration/fixtures/scheduler_retry_test.yaml
Normal file
207
tests/integration/fixtures/scheduler_retry_test.yaml
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
esphome:
|
||||||
|
name: scheduler-retry-test
|
||||||
|
on_boot:
|
||||||
|
priority: -100
|
||||||
|
then:
|
||||||
|
- logger.log: "Starting scheduler retry tests"
|
||||||
|
# Run all tests sequentially with delays
|
||||||
|
- script.execute: run_all_tests
|
||||||
|
|
||||||
|
host:
|
||||||
|
api:
|
||||||
|
logger:
|
||||||
|
level: VERBOSE
|
||||||
|
|
||||||
|
globals:
|
||||||
|
- id: simple_retry_counter
|
||||||
|
type: int
|
||||||
|
initial_value: '0'
|
||||||
|
- id: backoff_retry_counter
|
||||||
|
type: int
|
||||||
|
initial_value: '0'
|
||||||
|
- id: immediate_done_counter
|
||||||
|
type: int
|
||||||
|
initial_value: '0'
|
||||||
|
- id: cancel_retry_counter
|
||||||
|
type: int
|
||||||
|
initial_value: '0'
|
||||||
|
- id: empty_name_retry_counter
|
||||||
|
type: int
|
||||||
|
initial_value: '0'
|
||||||
|
- id: script_retry_counter
|
||||||
|
type: int
|
||||||
|
initial_value: '0'
|
||||||
|
- id: multiple_same_name_counter
|
||||||
|
type: int
|
||||||
|
initial_value: '0'
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
- platform: template
|
||||||
|
name: Test Sensor
|
||||||
|
id: test_sensor
|
||||||
|
lambda: return 1.0;
|
||||||
|
update_interval: never
|
||||||
|
|
||||||
|
script:
|
||||||
|
- id: run_all_tests
|
||||||
|
then:
|
||||||
|
# Test 1: Simple retry
|
||||||
|
- logger.log: "=== Test 1: Simple retry ==="
|
||||||
|
- lambda: |-
|
||||||
|
auto *component = id(test_sensor);
|
||||||
|
App.scheduler.set_retry(component, "simple_retry", 50, 3,
|
||||||
|
[](uint8_t retry_countdown) {
|
||||||
|
id(simple_retry_counter)++;
|
||||||
|
ESP_LOGI("test", "Simple retry attempt %d (countdown=%d)",
|
||||||
|
id(simple_retry_counter), retry_countdown);
|
||||||
|
|
||||||
|
if (id(simple_retry_counter) >= 2) {
|
||||||
|
ESP_LOGI("test", "Simple retry succeeded on attempt %d", id(simple_retry_counter));
|
||||||
|
return RetryResult::DONE;
|
||||||
|
}
|
||||||
|
return RetryResult::RETRY;
|
||||||
|
});
|
||||||
|
|
||||||
|
# Test 2: Backoff retry
|
||||||
|
- logger.log: "=== Test 2: Retry with backoff ==="
|
||||||
|
- lambda: |-
|
||||||
|
auto *component = id(test_sensor);
|
||||||
|
static uint32_t backoff_start_time = 0;
|
||||||
|
static uint32_t last_attempt_time = 0;
|
||||||
|
|
||||||
|
backoff_start_time = millis();
|
||||||
|
last_attempt_time = backoff_start_time;
|
||||||
|
|
||||||
|
App.scheduler.set_retry(component, "backoff_retry", 50, 4,
|
||||||
|
[](uint8_t retry_countdown) {
|
||||||
|
id(backoff_retry_counter)++;
|
||||||
|
uint32_t now = millis();
|
||||||
|
uint32_t interval = now - last_attempt_time;
|
||||||
|
last_attempt_time = now;
|
||||||
|
|
||||||
|
ESP_LOGI("test", "Backoff retry attempt %d (countdown=%d, interval=%dms)",
|
||||||
|
id(backoff_retry_counter), retry_countdown, interval);
|
||||||
|
|
||||||
|
if (id(backoff_retry_counter) == 1) {
|
||||||
|
ESP_LOGI("test", "First call was immediate");
|
||||||
|
} else if (id(backoff_retry_counter) == 2) {
|
||||||
|
ESP_LOGI("test", "Second call interval: %dms (expected ~50ms)", interval);
|
||||||
|
} else if (id(backoff_retry_counter) == 3) {
|
||||||
|
ESP_LOGI("test", "Third call interval: %dms (expected ~100ms)", interval);
|
||||||
|
} else if (id(backoff_retry_counter) == 4) {
|
||||||
|
ESP_LOGI("test", "Fourth call interval: %dms (expected ~200ms)", interval);
|
||||||
|
ESP_LOGI("test", "Backoff retry completed");
|
||||||
|
return RetryResult::DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RetryResult::RETRY;
|
||||||
|
}, 2.0f);
|
||||||
|
|
||||||
|
# Test 3: Immediate done
|
||||||
|
- logger.log: "=== Test 3: Immediate done ==="
|
||||||
|
- lambda: |-
|
||||||
|
auto *component = id(test_sensor);
|
||||||
|
App.scheduler.set_retry(component, "immediate_done", 50, 5,
|
||||||
|
[](uint8_t retry_countdown) {
|
||||||
|
id(immediate_done_counter)++;
|
||||||
|
ESP_LOGI("test", "Immediate done retry called (countdown=%d)", retry_countdown);
|
||||||
|
return RetryResult::DONE;
|
||||||
|
});
|
||||||
|
|
||||||
|
# Test 4: Cancel retry
|
||||||
|
- logger.log: "=== Test 4: Cancel retry ==="
|
||||||
|
- lambda: |-
|
||||||
|
auto *component = id(test_sensor);
|
||||||
|
App.scheduler.set_retry(component, "cancel_test", 25, 10,
|
||||||
|
[](uint8_t retry_countdown) {
|
||||||
|
id(cancel_retry_counter)++;
|
||||||
|
ESP_LOGI("test", "Cancel test retry attempt %d", id(cancel_retry_counter));
|
||||||
|
return RetryResult::RETRY;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cancel it after 100ms
|
||||||
|
App.scheduler.set_timeout(component, "cancel_timer", 100, []() {
|
||||||
|
bool cancelled = App.scheduler.cancel_retry(id(test_sensor), "cancel_test");
|
||||||
|
ESP_LOGI("test", "Retry cancellation result: %s", cancelled ? "true" : "false");
|
||||||
|
ESP_LOGI("test", "Cancel retry ran %d times before cancellation", id(cancel_retry_counter));
|
||||||
|
});
|
||||||
|
|
||||||
|
# Test 5: Empty name retry
|
||||||
|
- logger.log: "=== Test 5: Empty name retry ==="
|
||||||
|
- lambda: |-
|
||||||
|
auto *component = id(test_sensor);
|
||||||
|
App.scheduler.set_retry(component, "", 50, 5,
|
||||||
|
[](uint8_t retry_countdown) {
|
||||||
|
id(empty_name_retry_counter)++;
|
||||||
|
ESP_LOGI("test", "Empty name retry attempt %d", id(empty_name_retry_counter));
|
||||||
|
return RetryResult::RETRY;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Try to cancel after 75ms
|
||||||
|
App.scheduler.set_timeout(component, "empty_cancel_timer", 75, []() {
|
||||||
|
bool cancelled = App.scheduler.cancel_retry(id(test_sensor), "");
|
||||||
|
ESP_LOGI("test", "Empty name retry cancel result: %s",
|
||||||
|
cancelled ? "true" : "false");
|
||||||
|
ESP_LOGI("test", "Empty name retry ran %d times", id(empty_name_retry_counter));
|
||||||
|
});
|
||||||
|
|
||||||
|
# Test 6: Component method
|
||||||
|
- logger.log: "=== Test 6: Component::set_retry method ==="
|
||||||
|
- lambda: |-
|
||||||
|
class TestRetryComponent : public Component {
|
||||||
|
public:
|
||||||
|
void test_retry() {
|
||||||
|
this->set_retry(50, 3,
|
||||||
|
[](uint8_t retry_countdown) {
|
||||||
|
id(script_retry_counter)++;
|
||||||
|
ESP_LOGI("test", "Component retry attempt %d", id(script_retry_counter));
|
||||||
|
if (id(script_retry_counter) >= 2) {
|
||||||
|
return RetryResult::DONE;
|
||||||
|
}
|
||||||
|
return RetryResult::RETRY;
|
||||||
|
}, 1.5f);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static TestRetryComponent test_component;
|
||||||
|
test_component.test_retry();
|
||||||
|
|
||||||
|
# Test 7: Multiple same name
|
||||||
|
- logger.log: "=== Test 7: Multiple retries with same name ==="
|
||||||
|
- lambda: |-
|
||||||
|
auto *component = id(test_sensor);
|
||||||
|
|
||||||
|
// Set first retry
|
||||||
|
App.scheduler.set_retry(component, "duplicate_retry", 100, 5,
|
||||||
|
[](uint8_t retry_countdown) {
|
||||||
|
id(multiple_same_name_counter) += 1;
|
||||||
|
ESP_LOGI("test", "First duplicate retry - should not run");
|
||||||
|
return RetryResult::RETRY;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set second retry with same name (should cancel first)
|
||||||
|
App.scheduler.set_retry(component, "duplicate_retry", 50, 3,
|
||||||
|
[](uint8_t retry_countdown) {
|
||||||
|
id(multiple_same_name_counter) += 10;
|
||||||
|
ESP_LOGI("test", "Second duplicate retry attempt (counter=%d)",
|
||||||
|
id(multiple_same_name_counter));
|
||||||
|
if (id(multiple_same_name_counter) >= 20) {
|
||||||
|
return RetryResult::DONE;
|
||||||
|
}
|
||||||
|
return RetryResult::RETRY;
|
||||||
|
});
|
||||||
|
|
||||||
|
# Wait for all tests to complete before reporting
|
||||||
|
- delay: 500ms
|
||||||
|
|
||||||
|
# Final report
|
||||||
|
- logger.log: "=== Retry Test Results ==="
|
||||||
|
- lambda: |-
|
||||||
|
ESP_LOGI("test", "Simple retry counter: %d (expected 2)", id(simple_retry_counter));
|
||||||
|
ESP_LOGI("test", "Backoff retry counter: %d (expected 4)", id(backoff_retry_counter));
|
||||||
|
ESP_LOGI("test", "Immediate done counter: %d (expected 1)", id(immediate_done_counter));
|
||||||
|
ESP_LOGI("test", "Cancel retry counter: %d (expected ~3-4)", id(cancel_retry_counter));
|
||||||
|
ESP_LOGI("test", "Empty name retry counter: %d (expected 1-2)", id(empty_name_retry_counter));
|
||||||
|
ESP_LOGI("test", "Component retry counter: %d (expected 2)", id(script_retry_counter));
|
||||||
|
ESP_LOGI("test", "Multiple same name counter: %d (expected 20+)", id(multiple_same_name_counter));
|
||||||
|
ESP_LOGI("test", "All retry tests completed");
|
234
tests/integration/test_scheduler_retry_test.py
Normal file
234
tests/integration/test_scheduler_retry_test.py
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
"""Test scheduler retry functionality."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import re
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from .types import APIClientConnectedFactory, RunCompiledFunction
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_scheduler_retry_test(
|
||||||
|
yaml_config: str,
|
||||||
|
run_compiled: RunCompiledFunction,
|
||||||
|
api_client_connected: APIClientConnectedFactory,
|
||||||
|
) -> None:
|
||||||
|
"""Test that scheduler retry functionality works correctly."""
|
||||||
|
# Track test progress
|
||||||
|
simple_retry_done = asyncio.Event()
|
||||||
|
backoff_retry_done = asyncio.Event()
|
||||||
|
immediate_done_done = asyncio.Event()
|
||||||
|
cancel_retry_done = asyncio.Event()
|
||||||
|
empty_name_retry_done = asyncio.Event()
|
||||||
|
component_retry_done = asyncio.Event()
|
||||||
|
multiple_name_done = asyncio.Event()
|
||||||
|
test_complete = asyncio.Event()
|
||||||
|
|
||||||
|
# Track retry counts
|
||||||
|
simple_retry_count = 0
|
||||||
|
backoff_retry_count = 0
|
||||||
|
immediate_done_count = 0
|
||||||
|
cancel_retry_count = 0
|
||||||
|
empty_name_retry_count = 0
|
||||||
|
component_retry_count = 0
|
||||||
|
multiple_name_count = 0
|
||||||
|
|
||||||
|
# Track specific test results
|
||||||
|
cancel_result = None
|
||||||
|
empty_cancel_result = None
|
||||||
|
backoff_intervals = []
|
||||||
|
|
||||||
|
def on_log_line(line: str) -> None:
|
||||||
|
nonlocal simple_retry_count, backoff_retry_count, immediate_done_count
|
||||||
|
nonlocal cancel_retry_count, empty_name_retry_count, component_retry_count
|
||||||
|
nonlocal multiple_name_count, cancel_result, empty_cancel_result
|
||||||
|
|
||||||
|
# Strip ANSI color codes
|
||||||
|
clean_line = re.sub(r"\x1b\[[0-9;]*m", "", line)
|
||||||
|
|
||||||
|
# Simple retry test
|
||||||
|
if "Simple retry attempt" in clean_line:
|
||||||
|
if match := re.search(r"Simple retry attempt (\d+)", clean_line):
|
||||||
|
simple_retry_count = int(match.group(1))
|
||||||
|
|
||||||
|
elif "Simple retry succeeded on attempt" in clean_line:
|
||||||
|
simple_retry_done.set()
|
||||||
|
|
||||||
|
# Backoff retry test
|
||||||
|
elif "Backoff retry attempt" in clean_line:
|
||||||
|
if match := re.search(
|
||||||
|
r"Backoff retry attempt (\d+).*interval=(\d+)ms", clean_line
|
||||||
|
):
|
||||||
|
backoff_retry_count = int(match.group(1))
|
||||||
|
interval = int(match.group(2))
|
||||||
|
if backoff_retry_count > 1: # Skip first (immediate) call
|
||||||
|
backoff_intervals.append(interval)
|
||||||
|
|
||||||
|
elif "Backoff retry completed" in clean_line:
|
||||||
|
backoff_retry_done.set()
|
||||||
|
|
||||||
|
# Immediate done test
|
||||||
|
elif "Immediate done retry called" in clean_line:
|
||||||
|
immediate_done_count += 1
|
||||||
|
immediate_done_done.set()
|
||||||
|
|
||||||
|
# Cancel retry test
|
||||||
|
elif "Cancel test retry attempt" in clean_line:
|
||||||
|
cancel_retry_count += 1
|
||||||
|
|
||||||
|
elif "Retry cancellation result:" in clean_line:
|
||||||
|
cancel_result = "true" in clean_line
|
||||||
|
cancel_retry_done.set()
|
||||||
|
|
||||||
|
# Empty name retry test
|
||||||
|
elif "Empty name retry attempt" in clean_line:
|
||||||
|
if match := re.search(r"Empty name retry attempt (\d+)", clean_line):
|
||||||
|
empty_name_retry_count = int(match.group(1))
|
||||||
|
|
||||||
|
elif "Empty name retry cancel result:" in clean_line:
|
||||||
|
empty_cancel_result = "true" in clean_line
|
||||||
|
|
||||||
|
elif "Empty name retry ran" in clean_line:
|
||||||
|
empty_name_retry_done.set()
|
||||||
|
|
||||||
|
# Component retry test
|
||||||
|
elif "Component retry attempt" in clean_line:
|
||||||
|
if match := re.search(r"Component retry attempt (\d+)", clean_line):
|
||||||
|
component_retry_count = int(match.group(1))
|
||||||
|
if component_retry_count >= 2:
|
||||||
|
component_retry_done.set()
|
||||||
|
|
||||||
|
# Multiple same name test
|
||||||
|
elif "Second duplicate retry attempt" in clean_line:
|
||||||
|
if match := re.search(r"counter=(\d+)", clean_line):
|
||||||
|
multiple_name_count = int(match.group(1))
|
||||||
|
if multiple_name_count >= 20:
|
||||||
|
multiple_name_done.set()
|
||||||
|
|
||||||
|
# Test completion
|
||||||
|
elif "All retry tests completed" in clean_line:
|
||||||
|
test_complete.set()
|
||||||
|
|
||||||
|
async with (
|
||||||
|
run_compiled(yaml_config, line_callback=on_log_line),
|
||||||
|
api_client_connected() as client,
|
||||||
|
):
|
||||||
|
# Verify we can connect
|
||||||
|
device_info = await client.device_info()
|
||||||
|
assert device_info is not None
|
||||||
|
assert device_info.name == "scheduler-retry-test"
|
||||||
|
|
||||||
|
# Wait for simple retry test
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(simple_retry_done.wait(), timeout=1.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail(
|
||||||
|
f"Simple retry test did not complete. Count: {simple_retry_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert simple_retry_count == 2, (
|
||||||
|
f"Expected 2 simple retry attempts, got {simple_retry_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Wait for backoff retry test
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(backoff_retry_done.wait(), timeout=3.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail(
|
||||||
|
f"Backoff retry test did not complete. Count: {backoff_retry_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert backoff_retry_count == 4, (
|
||||||
|
f"Expected 4 backoff retry attempts, got {backoff_retry_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify backoff intervals (allowing for timing variations)
|
||||||
|
assert len(backoff_intervals) >= 2, (
|
||||||
|
f"Expected at least 2 intervals, got {len(backoff_intervals)}"
|
||||||
|
)
|
||||||
|
if len(backoff_intervals) >= 3:
|
||||||
|
# First interval should be ~50ms
|
||||||
|
assert 30 <= backoff_intervals[0] <= 70, (
|
||||||
|
f"First interval {backoff_intervals[0]}ms not ~50ms"
|
||||||
|
)
|
||||||
|
# Second interval should be ~100ms (50ms * 2.0)
|
||||||
|
assert 80 <= backoff_intervals[1] <= 120, (
|
||||||
|
f"Second interval {backoff_intervals[1]}ms not ~100ms"
|
||||||
|
)
|
||||||
|
# Third interval should be ~200ms (100ms * 2.0)
|
||||||
|
assert 180 <= backoff_intervals[2] <= 220, (
|
||||||
|
f"Third interval {backoff_intervals[2]}ms not ~200ms"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Wait for immediate done test
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(immediate_done_done.wait(), timeout=3.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail(
|
||||||
|
f"Immediate done test did not complete. Count: {immediate_done_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert immediate_done_count == 1, (
|
||||||
|
f"Expected 1 immediate done call, got {immediate_done_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Wait for cancel retry test
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(cancel_retry_done.wait(), timeout=2.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail(
|
||||||
|
f"Cancel retry test did not complete. Count: {cancel_retry_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert cancel_result is True, "Retry cancellation should have succeeded"
|
||||||
|
assert 2 <= cancel_retry_count <= 5, (
|
||||||
|
f"Expected 2-5 cancel retry attempts before cancellation, got {cancel_retry_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Wait for empty name retry test
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(empty_name_retry_done.wait(), timeout=1.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail(
|
||||||
|
f"Empty name retry test did not complete. Count: {empty_name_retry_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Empty name retry should run at least once before being cancelled
|
||||||
|
assert 1 <= empty_name_retry_count <= 2, (
|
||||||
|
f"Expected 1-2 empty name retry attempts, got {empty_name_retry_count}"
|
||||||
|
)
|
||||||
|
assert empty_cancel_result is True, (
|
||||||
|
"Empty name retry cancel should have succeeded"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Wait for component retry test
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(component_retry_done.wait(), timeout=1.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail(
|
||||||
|
f"Component retry test did not complete. Count: {component_retry_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert component_retry_count >= 2, (
|
||||||
|
f"Expected at least 2 component retry attempts, got {component_retry_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Wait for multiple same name test
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(multiple_name_done.wait(), timeout=1.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail(
|
||||||
|
f"Multiple same name test did not complete. Count: {multiple_name_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Should be 20+ (only second retry should run)
|
||||||
|
assert multiple_name_count >= 20, (
|
||||||
|
f"Expected multiple name count >= 20 (second retry only), got {multiple_name_count}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Wait for test completion
|
||||||
|
try:
|
||||||
|
await asyncio.wait_for(test_complete.wait(), timeout=1.0)
|
||||||
|
except TimeoutError:
|
||||||
|
pytest.fail("Test did not complete within timeout")
|
Loading…
x
Reference in New Issue
Block a user