From 80e3030811a84e4117131bcc9c9dcd2d701d45c1 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 10 Aug 2022 15:26:02 +1200 Subject: [PATCH 01/66] Bump version to 2022.9.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 6de2581fc8..e495775056 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2022.7.0-dev" +__version__ = "2022.9.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" From c7dc396b6d2328289935f153691bd9133297780d Mon Sep 17 00:00:00 2001 From: anatoly-savchenkov <48646998+anatoly-savchenkov@users.noreply.github.com> Date: Thu, 11 Aug 2022 04:32:55 +0300 Subject: [PATCH 02/66] Improve Web view for Climate components (#3706) --- esphome/components/web_server/server_index.h | 1136 +++++++++--------- esphome/components/web_server/web_server.cpp | 27 +- 2 files changed, 594 insertions(+), 569 deletions(-) diff --git a/esphome/components/web_server/server_index.h b/esphome/components/web_server/server_index.h index 719a804d0c..39c170f15b 100644 --- a/esphome/components/web_server/server_index.h +++ b/esphome/components/web_server/server_index.h @@ -6,568 +6,580 @@ namespace esphome { namespace web_server { const uint8_t INDEX_GZ[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xbd, 0x7d, 0xd9, 0x76, 0xe3, 0xc6, 0x92, 0xe0, 0xf3, - 0x9c, 0x33, 0x7f, 0x30, 0x2f, 0x10, 0xac, 0x56, 0x01, 0x16, 0x08, 0x91, 0x54, 0x6d, 0x06, 0x05, 0xf2, 0xca, 0x55, - 0xe5, 0x5b, 0x65, 0xd7, 0xe6, 0x92, 0xaa, 0xbc, 0xc8, 0x74, 0x09, 0x22, 0x93, 0x22, 0x5c, 0x20, 0x40, 0x03, 0x49, - 0x2d, 0xa6, 0xd0, 0xa7, 0x9f, 0xfa, 0x69, 0xce, 0x99, 0xf5, 0xa1, 0x5f, 0xe6, 0xf4, 0xcb, 0x7c, 0xc4, 0x7c, 0xcf, - 0xfd, 0x81, 0xe9, 0x4f, 0x98, 0x88, 0xc8, 0x05, 0x09, 0x90, 0x5a, 0xec, 0xf6, 0xdc, 0x53, 0x8b, 0x80, 0x5c, 0x23, - 0x23, 0x23, 0x63, 0x4f, 0x68, 0x6f, 0x63, 0x9c, 0x8d, 0xf8, 0xe5, 0x9c, 0x59, 0x53, 0x3e, 0x4b, 0xfa, 0x7b, 0xf2, - 0x7f, 0x16, 0x8d, 0xfb, 0x7b, 0x49, 0x9c, 0x7e, 0xb2, 0x72, 0x96, 0x84, 0xf1, 0x28, 0x4b, 0xad, 0x69, 0xce, 0x26, - 0xe1, 0x38, 0xe2, 0x51, 0x10, 0xcf, 0xa2, 0x53, 0x66, 0xed, 0xf4, 0xf7, 0x66, 0x8c, 0x47, 0xd6, 0x68, 0x1a, 0xe5, - 0x05, 0xe3, 0xe1, 0xfb, 0xc3, 0xaf, 0x5a, 0x8f, 0xfb, 0x7b, 0xc5, 0x28, 0x8f, 0xe7, 0xdc, 0xc2, 0x21, 0xc3, 0x59, - 0x36, 0x5e, 0x24, 0xac, 0x7f, 0x16, 0xe5, 0xd6, 0x3e, 0x0b, 0xdf, 0x9c, 0xfc, 0xc2, 0x46, 0xdc, 0x1f, 0xb3, 0x49, - 0x9c, 0xb2, 0xb7, 0x79, 0x36, 0x67, 0x39, 0xbf, 0xf4, 0x2e, 0xd6, 0x57, 0xc4, 0xac, 0xf0, 0x9e, 0xe9, 0xaa, 0x53, - 0xc6, 0xdf, 0x9c, 0xa7, 0xaa, 0xcf, 0x53, 0x26, 0x26, 0xc9, 0xf2, 0xc2, 0xe3, 0xd7, 0xb4, 0x39, 0xb8, 0x9c, 0x9d, - 0x64, 0x49, 0xe1, 0x1d, 0xe8, 0xfa, 0x79, 0x9e, 0xf1, 0x0c, 0xc1, 0xf2, 0xa7, 0x51, 0x61, 0xb4, 0xf4, 0x3e, 0xad, - 0x69, 0x32, 0x97, 0x95, 0x2f, 0x8a, 0x67, 0xe9, 0x62, 0xc6, 0xf2, 0xe8, 0x24, 0x61, 0x5e, 0xc1, 0x42, 0x87, 0x79, - 0xdc, 0x8b, 0xdd, 0xb0, 0xcf, 0xad, 0x38, 0xb5, 0xd8, 0x60, 0x9f, 0x51, 0xc9, 0x92, 0xe9, 0x56, 0xc1, 0x46, 0xdb, - 0x03, 0x74, 0x4d, 0xe2, 0xd3, 0x85, 0x7e, 0x3f, 0xcf, 0x63, 0xae, 0x9e, 0xcf, 0xa2, 0x64, 0xc1, 0x82, 0xb8, 0x74, - 0x03, 0x76, 0xc4, 0x87, 0x61, 0xec, 0x3d, 0xa1, 0x41, 0x61, 0xc8, 0xe5, 0x24, 0xcb, 0x1d, 0xc4, 0x55, 0x8c, 0x63, - 0xf3, 0xab, 0x2b, 0x87, 0x87, 0xcb, 0xd2, 0x75, 0x0f, 0x98, 0x3f, 0x8a, 0x92, 0xc4, 0xc1, 0x89, 0xb7, 0xb6, 0x0a, - 0x9c, 0x31, 0xf6, 0xf8, 0x51, 0x3c, 0x74, 0x7b, 0xf1, 0xc4, 0xe1, 0xcc, 0xad, 0xfa, 0x65, 0x13, 0x8b, 0x33, 0x87, - 0xbb, 0xee, 0xa7, 0xeb, 0xfb, 0xe4, 0x8c, 0x2f, 0x72, 0x80, 0xbd, 0xf4, 0xde, 0xa8, 0x99, 0x2f, 0xb0, 0xfe, 0x19, - 0x75, 0xec, 0x01, 0xec, 0x05, 0xb7, 0x3e, 0x84, 0xe7, 0x71, 0x3a, 0xce, 0xce, 0xfd, 0x83, 0x69, 0x04, 0x3f, 0xde, - 0x65, 0x19, 0xdf, 0xda, 0x72, 0xce, 0xb2, 0x78, 0x6c, 0xb5, 0xc3, 0xd0, 0xac, 0xbc, 0x7c, 0x72, 0x70, 0x70, 0x75, - 0xd5, 0x28, 0xf0, 0xd3, 0x88, 0xc7, 0x67, 0x4c, 0x74, 0x06, 0x00, 0x6c, 0xf8, 0x39, 0xe7, 0x6c, 0x7c, 0xc0, 0x2f, - 0x13, 0x28, 0x65, 0x8c, 0x17, 0x36, 0xac, 0xf1, 0x69, 0x36, 0x02, 0xb4, 0xa5, 0x06, 0xe2, 0xa1, 0x69, 0xce, 0xe6, - 0x49, 0x34, 0x62, 0x58, 0x0f, 0x23, 0x55, 0x3d, 0xaa, 0x46, 0xde, 0x57, 0xa1, 0xd8, 0x5e, 0xc7, 0xf5, 0x62, 0x16, - 0xa6, 0xec, 0xdc, 0x7a, 0x15, 0xcd, 0x7b, 0xa3, 0x24, 0x2a, 0x0a, 0xa0, 0xd7, 0x25, 0x2d, 0x21, 0x5f, 0x8c, 0x80, - 0x40, 0x68, 0x81, 0x4b, 0x44, 0xd3, 0x34, 0x2e, 0xfc, 0x8f, 0x9b, 0xa3, 0xa2, 0x78, 0xc7, 0x8a, 0x45, 0xc2, 0x37, - 0x43, 0xd8, 0x0b, 0xbe, 0x11, 0x86, 0x5f, 0xb9, 0x7c, 0x9a, 0x67, 0xe7, 0xd6, 0xb3, 0x3c, 0x87, 0xe6, 0x36, 0x4c, - 0x29, 0x1a, 0x58, 0x71, 0x61, 0xa5, 0x19, 0xb7, 0xf4, 0x60, 0xb8, 0x81, 0xbe, 0xf5, 0xbe, 0x60, 0xd6, 0xf1, 0x22, - 0x2d, 0xa2, 0x09, 0x83, 0xa6, 0xc7, 0x56, 0x96, 0x5b, 0xc7, 0x30, 0xe8, 0x31, 0x6c, 0x59, 0xc1, 0xe1, 0xd4, 0xf8, - 0xb6, 0xdb, 0xa3, 0xb9, 0xa0, 0xf0, 0x90, 0x5d, 0xf0, 0x90, 0x95, 0x40, 0x98, 0x56, 0xa1, 0x97, 0xe1, 0xb8, 0xcb, - 0x04, 0x0a, 0x58, 0x18, 0x33, 0x24, 0x59, 0xc7, 0x6c, 0xac, 0x37, 0xe7, 0xc3, 0xd6, 0x96, 0xc6, 0x35, 0xe0, 0xc4, - 0x81, 0xb6, 0x45, 0xa3, 0xad, 0x27, 0x16, 0x5e, 0x43, 0x91, 0xeb, 0x31, 0x5f, 0xa2, 0xef, 0xe0, 0x32, 0x1d, 0xd5, - 0xc7, 0x86, 0xca, 0x92, 0x67, 0x07, 0x3c, 0x8f, 0xd3, 0x53, 0x00, 0x42, 0xce, 0x64, 0x36, 0x29, 0x4b, 0xb1, 0xf9, - 0x4f, 0x58, 0xc8, 0xc2, 0x3e, 0x8e, 0x9e, 0x33, 0xc7, 0x2e, 0xa8, 0x87, 0x1d, 0x86, 0x88, 0x7a, 0x20, 0x30, 0x36, - 0x60, 0x01, 0xdb, 0xb6, 0x6d, 0xef, 0x2b, 0xd7, 0x3b, 0x47, 0x0a, 0xf2, 0x7d, 0x9f, 0xc8, 0x57, 0x74, 0x8e, 0xc3, - 0x0e, 0x02, 0xed, 0x27, 0x2c, 0x3d, 0xe5, 0xd3, 0x01, 0x3b, 0x6a, 0x0f, 0x03, 0x0e, 0x50, 0x8d, 0x17, 0x23, 0xe6, - 0x20, 0x3d, 0x7a, 0x05, 0x1e, 0x9f, 0x6d, 0x07, 0xa6, 0xc0, 0x8d, 0xd9, 0xa0, 0x35, 0xd6, 0xb6, 0xc6, 0x55, 0x24, - 0xaa, 0x00, 0x43, 0x3a, 0xb7, 0xe1, 0x84, 0x9d, 0xb0, 0xdc, 0x80, 0x43, 0x37, 0xeb, 0xd5, 0x76, 0x70, 0x01, 0x3b, - 0x04, 0xfd, 0xac, 0xc9, 0x22, 0x1d, 0xf1, 0x18, 0x18, 0x97, 0xbd, 0x0d, 0xe0, 0x8a, 0x9d, 0xd3, 0x1b, 0x67, 0xbb, - 0xa5, 0xeb, 0xc4, 0xee, 0x36, 0x3b, 0x2a, 0xb6, 0x3b, 0x43, 0x0f, 0xa1, 0xd4, 0xc8, 0x97, 0x0b, 0x8f, 0x61, 0x81, - 0x70, 0x46, 0x98, 0x3e, 0x9e, 0x1f, 0x06, 0xcc, 0x5f, 0xa5, 0xe3, 0x90, 0xfb, 0xb3, 0x68, 0x8e, 0xab, 0x61, 0x44, - 0x03, 0x51, 0x3a, 0x42, 0xe8, 0x6a, 0xfb, 0x82, 0x18, 0xf3, 0x2b, 0x12, 0x70, 0x01, 0x21, 0x70, 0x66, 0x9f, 0x45, - 0xa3, 0x29, 0x1c, 0xf1, 0x0a, 0x71, 0x63, 0x75, 0x1c, 0x46, 0x39, 0x8b, 0x38, 0x7b, 0x96, 0x30, 0x7c, 0xc3, 0x1d, - 0x80, 0x9e, 0xb6, 0xeb, 0x15, 0xea, 0xdc, 0x25, 0x31, 0x7f, 0x9d, 0xc1, 0x3c, 0x3d, 0x41, 0x24, 0x40, 0xc5, 0xc5, - 0xd6, 0x56, 0x8c, 0x24, 0xb2, 0xcf, 0x61, 0xb7, 0x4e, 0x16, 0xc0, 0x04, 0xec, 0x14, 0x5b, 0xd8, 0x80, 0x6d, 0x2f, - 0xf6, 0x39, 0x20, 0xf1, 0x49, 0x96, 0x72, 0x18, 0x0e, 0xe0, 0xd5, 0x14, 0xe4, 0x47, 0xf3, 0x39, 0x4b, 0xc7, 0x4f, - 0xa6, 0x71, 0x32, 0x06, 0x6c, 0x94, 0xb0, 0xde, 0x8c, 0x85, 0xb0, 0x4e, 0x58, 0x4c, 0x70, 0xf3, 0x8a, 0x68, 0xfb, - 0x90, 0x90, 0x79, 0x68, 0xdb, 0x3d, 0xe4, 0x40, 0x72, 0x15, 0xc8, 0x83, 0x68, 0xe3, 0xde, 0x01, 0xeb, 0x2f, 0x5c, - 0xbe, 0x1d, 0xc6, 0x7a, 0x1b, 0x25, 0x82, 0x9f, 0x20, 0xa7, 0x01, 0xfc, 0x33, 0xe0, 0x81, 0x3d, 0x64, 0x5c, 0xdf, - 0x49, 0xae, 0x93, 0x32, 0xb5, 0x42, 0x40, 0xc0, 0x08, 0x39, 0x88, 0xc4, 0xc1, 0xdb, 0x2c, 0xb9, 0x9c, 0xc4, 0x49, - 0x72, 0xb0, 0x98, 0xcf, 0xb3, 0x9c, 0x7b, 0x5f, 0x87, 0x4b, 0x9e, 0x55, 0x6b, 0xa5, 0x43, 0x5e, 0x9c, 0xc7, 0x1c, - 0x11, 0xea, 0x2e, 0x47, 0x11, 0x6c, 0xf5, 0x97, 0x59, 0x96, 0xb0, 0x28, 0x85, 0x65, 0xb0, 0x81, 0x6d, 0x07, 0xe9, - 0x22, 0x49, 0x7a, 0x27, 0x30, 0xec, 0xa7, 0x1e, 0x55, 0x0b, 0x8e, 0x1f, 0xd0, 0xf3, 0x7e, 0x9e, 0x47, 0x97, 0xd0, - 0x10, 0xdb, 0x00, 0x2d, 0xc2, 0x6e, 0x7d, 0x7d, 0xf0, 0xe6, 0xb5, 0x2f, 0x08, 0x3f, 0x9e, 0x5c, 0x02, 0xa0, 0x65, - 0xc5, 0x35, 0x27, 0x79, 0x36, 0x6b, 0x4c, 0x8d, 0x78, 0x88, 0x43, 0xd6, 0xbb, 0x06, 0x84, 0x98, 0x46, 0x86, 0x5d, - 0x62, 0x26, 0x04, 0xaf, 0x89, 0x9e, 0x65, 0x25, 0x9e, 0x81, 0x01, 0x3e, 0x04, 0xa2, 0x18, 0xa6, 0xbc, 0x19, 0x5a, - 0x9e, 0x5f, 0x2e, 0xe3, 0x90, 0xe0, 0x9c, 0xa3, 0xfc, 0x45, 0x18, 0x47, 0x11, 0xcc, 0xbe, 0x14, 0x03, 0x96, 0x0a, - 0xe2, 0xb8, 0x2c, 0xbd, 0x44, 0x13, 0x31, 0x72, 0x3c, 0x64, 0x28, 0x1c, 0x8e, 0xd1, 0xd5, 0x15, 0x83, 0x17, 0xd7, - 0xfb, 0x26, 0x5c, 0x46, 0x6a, 0x3d, 0x28, 0xa1, 0xf0, 0x7c, 0x05, 0x82, 0x4f, 0xa0, 0x24, 0x3b, 0x03, 0x39, 0x08, - 0x70, 0x7e, 0xed, 0x81, 0xfc, 0x4f, 0x10, 0x8a, 0x8d, 0x8e, 0x07, 0x12, 0xf4, 0xc9, 0x34, 0x4a, 0x4f, 0xd9, 0x38, - 0x48, 0x58, 0x29, 0x39, 0xef, 0xbe, 0x05, 0x7b, 0x0c, 0xe4, 0x54, 0x58, 0xcf, 0x0f, 0x5f, 0xbd, 0x94, 0x3b, 0x57, - 0x63, 0xc6, 0xb0, 0x49, 0x0b, 0x10, 0xab, 0xc0, 0xb6, 0x25, 0x3b, 0x7e, 0xc6, 0x15, 0xf7, 0x16, 0x25, 0x71, 0xf1, - 0x7e, 0x0e, 0x2a, 0x06, 0x7b, 0x0b, 0xc3, 0xc0, 0xf4, 0x21, 0x4c, 0x45, 0xe5, 0x30, 0x9f, 0xa8, 0x18, 0xeb, 0x22, - 0xe8, 0x2c, 0x56, 0x2a, 0x5e, 0x33, 0xc7, 0x2d, 0x81, 0x54, 0x79, 0x3c, 0xb2, 0xa2, 0xf1, 0xf8, 0x45, 0x1a, 0xf3, - 0x38, 0x4a, 0xe2, 0xdf, 0x08, 0x93, 0x4b, 0xa4, 0x31, 0xde, 0x93, 0x9b, 0x00, 0x6b, 0xa7, 0x1e, 0x89, 0xab, 0x98, - 0xec, 0x06, 0x21, 0x43, 0x70, 0xcb, 0x24, 0x3c, 0x1a, 0x4a, 0xf0, 0x12, 0x7f, 0xbe, 0x28, 0xa6, 0x88, 0x58, 0x39, - 0x30, 0x32, 0xf2, 0xec, 0xa4, 0x60, 0xf9, 0x19, 0x1b, 0x6b, 0x0a, 0x28, 0x60, 0x55, 0xd4, 0x1c, 0x94, 0x17, 0x9a, - 0xd1, 0x51, 0x32, 0x94, 0xc1, 0x50, 0x3d, 0x93, 0xcd, 0x32, 0x49, 0xcc, 0x5a, 0xc3, 0xd1, 0x5c, 0xc0, 0x11, 0x4a, - 0x85, 0xe4, 0x04, 0x45, 0xa8, 0x56, 0x38, 0x05, 0x2e, 0x04, 0x52, 0xc1, 0x3c, 0xe6, 0x4a, 0x92, 0x3d, 0x5b, 0x90, - 0x48, 0x28, 0xa0, 0x23, 0x1c, 0x64, 0x82, 0xb4, 0x70, 0xe1, 0x54, 0x01, 0x97, 0x97, 0xe0, 0x0a, 0x2e, 0xa2, 0xd4, - 0x1c, 0x24, 0x80, 0xf0, 0x1b, 0x21, 0x0b, 0x7d, 0x6c, 0x41, 0x64, 0xe0, 0xeb, 0x9d, 0x07, 0xc4, 0xca, 0x75, 0x57, - 0x0b, 0xf1, 0xae, 0x01, 0x1b, 0x27, 0x46, 0x7a, 0xf2, 0x36, 0xb8, 0x9f, 0x66, 0xfb, 0xa3, 0x11, 0x2b, 0x8a, 0x2c, - 0xdf, 0xda, 0xda, 0xa0, 0xf6, 0xd7, 0x29, 0x5a, 0x80, 0x49, 0x57, 0xf3, 0x3a, 0xbb, 0x20, 0x09, 0x6e, 0x8a, 0x15, - 0x25, 0xd3, 0x03, 0xfb, 0xe3, 0x47, 0xe0, 0xd9, 0x9e, 0x44, 0x03, 0x60, 0x7d, 0x55, 0xf1, 0x13, 0xfa, 0x4c, 0x1d, - 0x33, 0x6b, 0xf5, 0x4b, 0xa7, 0x0e, 0x92, 0x07, 0xc3, 0xba, 0xa5, 0xb1, 0xa1, 0x6b, 0x87, 0xc6, 0xdd, 0x90, 0x02, - 0x72, 0x79, 0x4a, 0x22, 0xdb, 0xd8, 0x46, 0xd0, 0xda, 0x4a, 0x8f, 0x50, 0xaf, 0x56, 0x93, 0x13, 0xa0, 0x47, 0x6c, - 0xd8, 0x93, 0xf5, 0x61, 0x21, 0x30, 0x97, 0xb3, 0x5f, 0x17, 0xac, 0xe0, 0x82, 0x74, 0x61, 0xdc, 0x1c, 0xc6, 0x2d, - 0x57, 0xb4, 0xc3, 0x9a, 0xee, 0xb8, 0x0e, 0xb6, 0x37, 0x73, 0x94, 0x63, 0x05, 0x52, 0xf2, 0xcd, 0xe4, 0x84, 0xb0, - 0x32, 0xf7, 0xea, 0xea, 0x1b, 0x35, 0x48, 0xb5, 0x95, 0x5a, 0x07, 0x6a, 0xec, 0x89, 0xad, 0x9a, 0x8c, 0x6d, 0x57, - 0x0a, 0xd4, 0x8d, 0x4e, 0xaf, 0x46, 0x07, 0x70, 0xe6, 0xda, 0x9a, 0xa4, 0x2b, 0x65, 0xfb, 0xad, 0xc2, 0xe9, 0x1b, - 0x31, 0x32, 0x69, 0xa3, 0xec, 0x76, 0xea, 0x51, 0x27, 0x1e, 0xda, 0xae, 0xd4, 0x55, 0x8c, 0x61, 0x51, 0x67, 0x0c, - 0x4d, 0xa8, 0xe7, 0xba, 0x8b, 0xad, 0x89, 0x8a, 0x85, 0x6a, 0xaf, 0x95, 0x01, 0xc1, 0xc3, 0x23, 0x50, 0x4e, 0xd6, - 0xda, 0x07, 0xaf, 0xa3, 0x19, 0x43, 0x8c, 0x7a, 0xd7, 0x35, 0x90, 0x06, 0x04, 0x34, 0x19, 0x36, 0xc5, 0x1b, 0x77, - 0x85, 0xd6, 0x54, 0x3f, 0x5f, 0x31, 0x68, 0x11, 0xa0, 0x5f, 0x97, 0x6b, 0xb6, 0x88, 0xe4, 0xa6, 0x24, 0x67, 0x85, - 0x1f, 0x51, 0x26, 0xf6, 0x84, 0x04, 0x3c, 0x2c, 0x1e, 0xb6, 0xbf, 0xb1, 0x71, 0xb2, 0x15, 0x53, 0x6b, 0xe4, 0xc8, - 0x53, 0x00, 0xcf, 0x24, 0x04, 0x80, 0x5d, 0xd2, 0xcf, 0xda, 0xc1, 0x42, 0xb4, 0x1d, 0x20, 0x1d, 0xf8, 0x93, 0x24, - 0xe2, 0x4e, 0x67, 0xa7, 0xed, 0x02, 0x1d, 0x02, 0x13, 0x07, 0x19, 0x01, 0xea, 0x7d, 0xb5, 0x14, 0x86, 0x4b, 0x89, - 0x5d, 0xee, 0x83, 0x52, 0x34, 0x8d, 0x27, 0xdc, 0xc9, 0x50, 0x88, 0xb8, 0x25, 0x4b, 0x40, 0xc8, 0xe8, 0x73, 0x05, - 0x5c, 0x82, 0x0b, 0xee, 0x22, 0xaa, 0x35, 0x43, 0x53, 0x90, 0x12, 0x97, 0x22, 0x29, 0xa8, 0x20, 0x30, 0x98, 0x4a, - 0x4f, 0x51, 0x14, 0xc8, 0xb7, 0x78, 0x20, 0x06, 0x0d, 0x56, 0x34, 0xca, 0x78, 0x10, 0xaf, 0x16, 0x82, 0x18, 0xf6, - 0x79, 0xf6, 0x32, 0x3b, 0x67, 0xf9, 0x93, 0x08, 0x61, 0x0f, 0x44, 0xf7, 0x12, 0x38, 0x3d, 0x31, 0x74, 0xd6, 0x53, - 0xb4, 0x72, 0x46, 0x8b, 0x86, 0x8d, 0x98, 0xc5, 0x28, 0x08, 0x41, 0xca, 0x11, 0xee, 0x53, 0x3c, 0x52, 0x74, 0xf6, - 0x50, 0x94, 0x30, 0x4d, 0x5b, 0xfb, 0x2f, 0xeb, 0xb4, 0x05, 0x23, 0xcc, 0x15, 0xb5, 0xd6, 0x4f, 0xac, 0xeb, 0x49, - 0xd9, 0xec, 0x48, 0xda, 0x32, 0x84, 0x19, 0xc8, 0x8f, 0xab, 0xab, 0x4a, 0x49, 0x07, 0x61, 0xaa, 0xb9, 0x39, 0x6a, - 0x4e, 0xe2, 0x48, 0xb8, 0x25, 0x08, 0x23, 0x54, 0xbc, 0xf2, 0x2c, 0x49, 0x0c, 0x59, 0xe4, 0xc5, 0x3d, 0xa7, 0x21, - 0x8e, 0x00, 0x8a, 0x59, 0x4d, 0x22, 0x0d, 0x78, 0xa0, 0x2b, 0x50, 0x28, 0x29, 0x69, 0xe4, 0x55, 0x4d, 0x04, 0xc4, - 0xe9, 0x98, 0xe5, 0xc2, 0x40, 0x93, 0x32, 0x14, 0x26, 0x4c, 0x81, 0xa0, 0xd9, 0x18, 0x38, 0xbc, 0x5a, 0x00, 0xa8, - 0x27, 0xfe, 0x34, 0x2b, 0xb8, 0xae, 0x33, 0xa1, 0x8f, 0xaf, 0xae, 0x62, 0x61, 0x2f, 0x22, 0x01, 0xe4, 0x6c, 0x96, - 0x9d, 0xb1, 0x35, 0x50, 0xf7, 0xd4, 0x60, 0x26, 0xc8, 0xc6, 0x30, 0x20, 0x44, 0x41, 0xb4, 0xcc, 0x93, 0x78, 0xc4, - 0xb4, 0x94, 0x9a, 0xf9, 0xa0, 0xd0, 0xb1, 0x0b, 0xe0, 0x11, 0xcc, 0xed, 0xf7, 0xfb, 0x6d, 0xaf, 0xe3, 0x96, 0x02, - 0xe1, 0xcb, 0x15, 0x8c, 0xde, 0x20, 0x1f, 0xa5, 0x0a, 0xbe, 0x8e, 0x17, 0x70, 0xd7, 0x10, 0x8a, 0x5c, 0xd8, 0x49, - 0x9e, 0x64, 0xc4, 0xae, 0x37, 0x86, 0x41, 0x39, 0x53, 0x8c, 0x1b, 0x55, 0x5c, 0x71, 0x6c, 0xdf, 0x69, 0xb4, 0x69, - 0x72, 0x52, 0x27, 0x4c, 0x6d, 0x8c, 0xdc, 0xf3, 0x42, 0x5b, 0xc0, 0xe6, 0xf6, 0xa0, 0x96, 0x48, 0xd5, 0x40, 0xeb, - 0x00, 0xa1, 0xb0, 0x74, 0x9d, 0x95, 0x25, 0x55, 0x9d, 0x25, 0x13, 0xd7, 0x07, 0xe8, 0x0d, 0x93, 0x60, 0xae, 0x43, - 0xc1, 0x81, 0x64, 0x08, 0x1c, 0x2d, 0x32, 0xb1, 0x5f, 0x4f, 0x60, 0x7b, 0x4e, 0xa2, 0xd1, 0x27, 0x0d, 0x6e, 0x85, - 0xf6, 0x26, 0x19, 0x38, 0x8d, 0x92, 0xd0, 0x60, 0x57, 0xe6, 0xba, 0x15, 0x87, 0xae, 0x1d, 0x14, 0x30, 0xc8, 0x56, - 0xc8, 0xbe, 0xb9, 0xd1, 0x4d, 0x6a, 0x97, 0xe4, 0xa1, 0xec, 0x27, 0x4d, 0x25, 0x37, 0x90, 0x1c, 0x57, 0xdc, 0x80, - 0x2b, 0xc2, 0x83, 0xad, 0x69, 0x40, 0x02, 0x74, 0x57, 0x8e, 0xe3, 0xe2, 0x7a, 0x14, 0xfc, 0xa9, 0x60, 0x3e, 0x35, - 0x66, 0xba, 0x15, 0x52, 0xcd, 0xe1, 0xa4, 0x1a, 0xac, 0x41, 0x93, 0xca, 0x83, 0x62, 0x35, 0xdf, 0xa0, 0xa2, 0x42, - 0x14, 0x7f, 0x2a, 0xaa, 0x50, 0x05, 0x43, 0x30, 0x0a, 0x2f, 0x97, 0x04, 0x97, 0xad, 0xb2, 0x16, 0xc9, 0x53, 0x63, - 0x12, 0xa9, 0x9a, 0xe4, 0x32, 0x50, 0xb0, 0xe8, 0xb4, 0xfa, 0x52, 0x13, 0x57, 0x2c, 0x37, 0x0d, 0x35, 0x33, 0xc9, - 0x95, 0x35, 0xe1, 0x14, 0x68, 0x77, 0x29, 0xed, 0xdd, 0x5c, 0x4f, 0xa1, 0xd6, 0x53, 0xf8, 0x86, 0x0d, 0x65, 0xd2, - 0x76, 0x3e, 0x00, 0x75, 0xbf, 0x56, 0x89, 0xfa, 0xa9, 0x8f, 0x8c, 0xd9, 0xd5, 0x4c, 0x17, 0x18, 0x8a, 0x24, 0x93, - 0x74, 0x20, 0xe9, 0x0d, 0xd9, 0x46, 0x65, 0x19, 0x65, 0xae, 0x38, 0x20, 0x35, 0xab, 0x34, 0xf3, 0x52, 0xb7, 0xa1, - 0xbf, 0x97, 0xa5, 0xc4, 0x13, 0x17, 0x98, 0x89, 0xbd, 0x9b, 0x70, 0xe3, 0xa5, 0x61, 0x26, 0xb4, 0x5f, 0xa1, 0xec, - 0xd4, 0x30, 0x94, 0x4a, 0x16, 0x88, 0x63, 0xe3, 0x6b, 0xa5, 0x19, 0x64, 0xfe, 0x1a, 0x7d, 0x0a, 0x40, 0x49, 0x60, - 0xf3, 0x35, 0x96, 0xbc, 0x28, 0xac, 0xe3, 0x71, 0x83, 0xf0, 0x58, 0xb1, 0xd0, 0x1a, 0xcb, 0xd7, 0xf2, 0x2c, 0xf6, - 0x6b, 0x26, 0xa1, 0x89, 0xc9, 0x62, 0x50, 0x04, 0xb6, 0x72, 0x44, 0x54, 0xb2, 0x2d, 0x19, 0x24, 0x64, 0x90, 0xae, - 0x22, 0xbd, 0x36, 0x92, 0x81, 0xeb, 0x54, 0x70, 0xb4, 0x74, 0x18, 0x46, 0x0e, 0x1a, 0xee, 0xb4, 0x17, 0x2b, 0x88, - 0x6c, 0xea, 0x9b, 0x44, 0x8a, 0x68, 0x9c, 0x16, 0xa8, 0xc2, 0x99, 0x32, 0xdd, 0x71, 0x60, 0x39, 0xc0, 0xf6, 0x57, - 0x48, 0x6f, 0xad, 0xda, 0xe9, 0xfa, 0x95, 0xc1, 0x77, 0x75, 0x95, 0x20, 0x3d, 0x08, 0x85, 0x17, 0xf6, 0x6c, 0xa0, - 0x78, 0xef, 0xfe, 0x4b, 0x6c, 0x45, 0xfa, 0x67, 0x55, 0x52, 0x59, 0x0a, 0x35, 0xca, 0xad, 0xef, 0x13, 0x33, 0x5d, - 0x8b, 0xaa, 0xe2, 0xc0, 0xe0, 0xea, 0x07, 0x4a, 0x60, 0x57, 0x4b, 0x3e, 0x90, 0x43, 0xc7, 0xae, 0xeb, 0x06, 0x05, - 0x19, 0x2f, 0x1b, 0xeb, 0x4c, 0xc8, 0xad, 0x2d, 0xd3, 0x66, 0x3a, 0xd3, 0xc3, 0x3f, 0x71, 0x50, 0x38, 0x17, 0x97, - 0x29, 0x69, 0x30, 0x4f, 0x94, 0x38, 0x5a, 0x31, 0x40, 0xdb, 0x3d, 0xb4, 0xb4, 0xa3, 0xf3, 0x28, 0xe6, 0x96, 0x1e, - 0x45, 0x58, 0xda, 0xc8, 0x9f, 0xa4, 0xd2, 0x01, 0xeb, 0x42, 0x15, 0x92, 0x8c, 0x70, 0x53, 0x17, 0x2d, 0x46, 0x53, - 0x86, 0x2e, 0x70, 0xa5, 0x4f, 0x98, 0xbc, 0x67, 0x03, 0xd7, 0x2d, 0x06, 0x66, 0xeb, 0x61, 0x2f, 0x9b, 0xdd, 0x6b, - 0xea, 0x3f, 0xec, 0x11, 0xf0, 0xb6, 0x99, 0xaa, 0x2b, 0x1b, 0xef, 0x92, 0x45, 0xa2, 0x87, 0x6d, 0xdd, 0xd8, 0x52, - 0xd7, 0xef, 0x35, 0xcc, 0xeb, 0xca, 0x30, 0xaf, 0x09, 0xd5, 0x86, 0x1c, 0x56, 0x66, 0x0e, 0x33, 0x0d, 0x79, 0xb1, - 0x83, 0x6e, 0x4f, 0x38, 0x85, 0xc0, 0x88, 0xd0, 0xfa, 0xa0, 0xa2, 0x06, 0x42, 0x25, 0x57, 0x52, 0x35, 0x5b, 0x24, - 0x63, 0x09, 0x2c, 0x98, 0xb0, 0x5c, 0xd2, 0xd1, 0x79, 0x9c, 0x24, 0x55, 0xe9, 0x9f, 0xca, 0xe0, 0xc5, 0xb0, 0xb7, - 0xb1, 0x76, 0xb1, 0xa2, 0x85, 0x02, 0xc1, 0xd5, 0x4a, 0xd8, 0x7b, 0xc7, 0xad, 0xf6, 0x5d, 0x78, 0x1c, 0xb9, 0xe9, - 0x8d, 0x80, 0x7a, 0xf4, 0xb0, 0x6a, 0xd2, 0xde, 0x7f, 0x86, 0x2e, 0x35, 0x63, 0x3d, 0x28, 0xce, 0xa8, 0xf8, 0x77, - 0xe9, 0x53, 0xbf, 0x73, 0x79, 0xb7, 0x8a, 0xae, 0xa6, 0x43, 0x45, 0x39, 0x3e, 0x4c, 0x17, 0x4b, 0x5b, 0x39, 0x02, - 0x72, 0x3d, 0x2c, 0x72, 0x01, 0x13, 0x35, 0x58, 0x50, 0x8a, 0x55, 0x6b, 0x61, 0xf7, 0xf2, 0x36, 0x67, 0x0e, 0xb9, - 0xc2, 0x45, 0xff, 0x27, 0xd9, 0x6c, 0x8e, 0x9a, 0x59, 0x83, 0xa8, 0xa1, 0xc1, 0xfb, 0x46, 0x7d, 0xb9, 0xa6, 0xac, - 0xd6, 0x87, 0x4e, 0x64, 0x8d, 0x9e, 0xb4, 0xa1, 0x0c, 0x06, 0xd5, 0x42, 0x17, 0xd5, 0xf5, 0xe6, 0x26, 0x8b, 0x59, - 0x47, 0xe3, 0x3e, 0xc9, 0x6d, 0xad, 0x4d, 0x7a, 0x1a, 0x07, 0xc4, 0x93, 0x24, 0xc1, 0x9b, 0x04, 0x50, 0x56, 0xc8, - 0x59, 0x96, 0x0d, 0xf4, 0x2d, 0xcb, 0x12, 0xf7, 0xef, 0xdb, 0xde, 0x7e, 0xcd, 0xb2, 0xf6, 0xf6, 0xaf, 0x37, 0x91, - 0xab, 0x3a, 0x69, 0x41, 0x1e, 0x0d, 0xa1, 0x68, 0x45, 0xa7, 0x0c, 0x97, 0xb3, 0x6c, 0xcc, 0x02, 0x1b, 0xba, 0xa7, - 0x76, 0xa9, 0xa4, 0x32, 0x1c, 0x8e, 0x94, 0x39, 0xcb, 0x77, 0x75, 0x4f, 0x6a, 0xb0, 0x0f, 0x24, 0xa0, 0xd5, 0x85, - 0xef, 0xc2, 0xd3, 0x24, 0x3b, 0x89, 0x92, 0x43, 0x21, 0xc0, 0x6b, 0x2d, 0x3f, 0x80, 0xc9, 0x48, 0x1a, 0xab, 0x21, - 0xa4, 0xbe, 0x1b, 0x7c, 0x17, 0xdc, 0xde, 0xa3, 0xb2, 0x56, 0xec, 0x8e, 0xdf, 0xf6, 0x3b, 0xb6, 0xf2, 0x88, 0xbd, - 0x34, 0xa7, 0x03, 0x89, 0x53, 0x00, 0x66, 0x0e, 0x41, 0x92, 0x15, 0x5e, 0xc4, 0xc2, 0x97, 0x83, 0x97, 0xca, 0xa4, - 0xce, 0xc0, 0x84, 0x00, 0x23, 0x3f, 0x89, 0x79, 0x0b, 0xe3, 0x91, 0xb6, 0xb7, 0x14, 0x15, 0xe8, 0x57, 0x24, 0xbf, - 0x74, 0xa9, 0xac, 0x41, 0xef, 0x63, 0x78, 0x0c, 0xcd, 0x36, 0x37, 0x97, 0xce, 0xab, 0x88, 0x4f, 0xfd, 0x3c, 0x4a, - 0xc7, 0xd9, 0xcc, 0x71, 0xb7, 0x6d, 0xdb, 0xf5, 0x0b, 0xb2, 0x44, 0xbe, 0x70, 0xcb, 0xcd, 0x63, 0x6f, 0xca, 0x42, - 0x7b, 0x60, 0x6f, 0x7f, 0xf4, 0xde, 0xb2, 0xf0, 0x78, 0x6f, 0x73, 0x39, 0x65, 0x65, 0xff, 0xd8, 0xbb, 0xd0, 0x3e, - 0x77, 0xef, 0x2d, 0x72, 0x19, 0xe8, 0x15, 0xf6, 0x2f, 0x24, 0x18, 0x40, 0x6e, 0xe4, 0x7f, 0x07, 0x2e, 0xf7, 0x9e, - 0x02, 0x22, 0xd2, 0x4f, 0x7b, 0x75, 0x65, 0x67, 0xe4, 0x31, 0xb0, 0x37, 0xb4, 0xb1, 0xba, 0xb5, 0x55, 0x89, 0xf9, - 0xaa, 0xd4, 0x1b, 0xb1, 0xb0, 0x66, 0xa9, 0x7b, 0xef, 0x29, 0xb4, 0x52, 0x3f, 0xc8, 0x23, 0x46, 0x42, 0x73, 0x55, - 0x4f, 0x70, 0x8c, 0x23, 0xbe, 0xfe, 0x58, 0x1f, 0x09, 0x2f, 0x85, 0x1f, 0x83, 0xf6, 0x12, 0x81, 0xf8, 0x06, 0x03, - 0xc7, 0x3b, 0x0c, 0x77, 0xf6, 0x9c, 0x41, 0xe0, 0x6c, 0xb4, 0x5a, 0x57, 0x3f, 0xed, 0x1c, 0xfd, 0x1c, 0xb5, 0x7e, - 0xdb, 0x6f, 0xfd, 0x38, 0x74, 0xaf, 0x9c, 0x9f, 0x76, 0x06, 0x47, 0xf2, 0xed, 0xe8, 0xe7, 0xfe, 0x4f, 0xc5, 0xf0, - 0x73, 0x51, 0xb8, 0xe9, 0xba, 0x3b, 0xa7, 0x60, 0x29, 0x85, 0x3b, 0xad, 0x56, 0x1f, 0x9e, 0x16, 0xf0, 0x84, 0x3f, - 0x2f, 0xe1, 0xc7, 0xd5, 0x91, 0xf5, 0x1f, 0x7e, 0x4a, 0xff, 0xe3, 0x4f, 0xf9, 0x10, 0xc7, 0x3c, 0xfa, 0xf9, 0xa7, - 0xc2, 0xbe, 0xd7, 0x0f, 0x77, 0x86, 0xdb, 0xae, 0xa3, 0x6b, 0x3e, 0x0f, 0xab, 0x47, 0x68, 0x75, 0xf4, 0xb3, 0x7c, - 0xb3, 0xef, 0x1d, 0xef, 0xf5, 0xc3, 0xe1, 0x95, 0x63, 0x5f, 0xdd, 0x73, 0xaf, 0x5c, 0xf7, 0x6a, 0x13, 0xe7, 0x99, - 0xc3, 0xe8, 0xf7, 0xe0, 0xe7, 0x19, 0xfc, 0xb4, 0xe1, 0xe7, 0x29, 0xfc, 0xfc, 0x19, 0xba, 0x09, 0xff, 0xdb, 0x15, - 0xf9, 0x42, 0xae, 0x30, 0x60, 0x11, 0xc1, 0x2e, 0xb8, 0x9b, 0x3b, 0xb1, 0x37, 0x21, 0xa4, 0xc1, 0x39, 0xf4, 0x7d, - 0x1f, 0xdd, 0xa4, 0xce, 0xf2, 0xe3, 0x26, 0x6c, 0x3a, 0x52, 0xce, 0x66, 0xc0, 0x3c, 0xe1, 0x39, 0x28, 0x02, 0x2e, - 0x62, 0xab, 0x05, 0x06, 0x57, 0xbd, 0x45, 0x38, 0x61, 0x0e, 0x28, 0x05, 0x87, 0x0c, 0x1f, 0xba, 0xae, 0xf7, 0x4c, - 0xc6, 0x0c, 0xf1, 0x9c, 0x0b, 0xd2, 0x4a, 0x33, 0xa1, 0xd2, 0xd8, 0xae, 0x37, 0x5f, 0x53, 0x09, 0xc7, 0x3a, 0x3d, - 0x85, 0xba, 0x4d, 0x11, 0x68, 0xfb, 0x8e, 0x45, 0x9f, 0xf0, 0x48, 0x3e, 0x37, 0x82, 0xc0, 0x2b, 0x9a, 0x7c, 0x53, - 0x69, 0x34, 0x74, 0x44, 0x61, 0x8e, 0x7d, 0xc9, 0x60, 0x86, 0x15, 0x15, 0x91, 0x93, 0xd0, 0x14, 0x9a, 0x2d, 0x4c, - 0xfe, 0x36, 0xca, 0xf9, 0x66, 0xa5, 0xd8, 0x86, 0x35, 0x4d, 0xb6, 0xa9, 0xe9, 0xdf, 0x61, 0x0a, 0x54, 0x2d, 0x29, - 0xfe, 0x61, 0x8e, 0x1f, 0xa6, 0xb4, 0xac, 0xd7, 0x0e, 0x07, 0x0b, 0xbd, 0x00, 0xbe, 0x23, 0xfa, 0x39, 0x6f, 0x51, - 0x8c, 0xc1, 0x5f, 0xe9, 0x66, 0xf0, 0xc4, 0x7c, 0xe8, 0xa2, 0x59, 0x96, 0xda, 0xb9, 0x95, 0x22, 0xbb, 0x7f, 0x81, - 0x27, 0x23, 0x2d, 0xbd, 0x83, 0x50, 0x9d, 0x98, 0xc3, 0x9c, 0xb1, 0xef, 0xa2, 0xe4, 0x13, 0xcb, 0x9d, 0x0b, 0xaf, - 0xd3, 0xfd, 0x82, 0x3a, 0x7b, 0xa8, 0x9b, 0xbd, 0xae, 0xc2, 0x68, 0x4a, 0x2d, 0x50, 0x21, 0xc2, 0x56, 0xc7, 0x43, - 0x8e, 0x41, 0x28, 0xc8, 0xbd, 0x2c, 0xec, 0x12, 0x85, 0xdb, 0x7b, 0xc5, 0xd9, 0x69, 0xdf, 0x0e, 0x6c, 0x1b, 0x34, - 0xfe, 0x43, 0x72, 0x5b, 0x09, 0xc5, 0x02, 0x14, 0xb2, 0xbd, 0xb8, 0xc7, 0xb7, 0xb7, 0x2b, 0x87, 0x13, 0x06, 0xd2, - 0xa9, 0x7b, 0xe2, 0x45, 0xde, 0x34, 0x84, 0x01, 0x47, 0xd0, 0x0c, 0xbb, 0xf4, 0x46, 0x7b, 0xb1, 0x9c, 0x06, 0x7d, - 0x21, 0x7e, 0x12, 0x15, 0xfc, 0x05, 0xfa, 0x23, 0xc2, 0x11, 0x2a, 0xfb, 0x3e, 0xbb, 0x60, 0x23, 0xa5, 0x67, 0x00, - 0xa2, 0x22, 0xb7, 0xe7, 0x8e, 0x42, 0xa3, 0x19, 0xcc, 0x1d, 0x86, 0x87, 0x03, 0x1b, 0xce, 0x12, 0x9c, 0xca, 0x30, - 0x3a, 0xea, 0x0c, 0x07, 0x69, 0x08, 0xbc, 0x56, 0xe3, 0x56, 0x16, 0x2d, 0x6a, 0x45, 0xdd, 0xe1, 0xc0, 0x39, 0x05, - 0x25, 0x1d, 0x74, 0x71, 0x07, 0xdf, 0xd0, 0x43, 0x91, 0x87, 0xef, 0xd8, 0xe9, 0xb3, 0x8b, 0xb9, 0x63, 0xef, 0xed, - 0xd8, 0xdb, 0x58, 0xea, 0xd9, 0x40, 0x5e, 0x30, 0x77, 0x78, 0xe9, 0x9a, 0x9d, 0x77, 0x87, 0x08, 0x2a, 0x16, 0xe2, - 0xe4, 0x97, 0x03, 0xbb, 0x2f, 0xa6, 0x6e, 0xc3, 0xa0, 0xa9, 0xdc, 0x7e, 0xdc, 0xd1, 0x43, 0x5a, 0xaa, 0xea, 0xaa, - 0xa0, 0x83, 0xb2, 0x6e, 0xe0, 0x4c, 0xcd, 0x45, 0xb4, 0x70, 0x32, 0x89, 0x05, 0x30, 0x78, 0xb0, 0x19, 0x4c, 0x6a, - 0x74, 0xdb, 0x1d, 0x0e, 0x2e, 0x83, 0x7b, 0xf6, 0x3d, 0xf5, 0x72, 0xc6, 0x02, 0xb0, 0x2e, 0x68, 0xfa, 0x33, 0x94, - 0x22, 0xf0, 0x73, 0xce, 0x60, 0x91, 0x97, 0x54, 0x34, 0x96, 0x45, 0x0b, 0x2c, 0x3a, 0x0c, 0x10, 0x54, 0x2f, 0xd7, - 0xda, 0x9f, 0xd8, 0x93, 0x71, 0x48, 0xb0, 0x6f, 0x6d, 0xc1, 0xd6, 0x6c, 0x77, 0x86, 0x18, 0x6f, 0xc8, 0x79, 0xf1, - 0x5d, 0xcc, 0x41, 0x24, 0xec, 0xf4, 0x6d, 0x77, 0x60, 0x5b, 0xb8, 0xb5, 0xbd, 0x6c, 0x3b, 0x14, 0x18, 0x8e, 0xb7, - 0xdf, 0xb2, 0x60, 0xda, 0x0f, 0xdb, 0x03, 0xa7, 0x10, 0xa2, 0x23, 0xc1, 0xb8, 0xa5, 0xe0, 0xe0, 0x6d, 0x6f, 0x0a, - 0x0c, 0x1d, 0x29, 0x77, 0xd3, 0xde, 0x56, 0x85, 0x50, 0xf4, 0x71, 0x7b, 0xec, 0x06, 0x31, 0xfc, 0x70, 0x5a, 0x48, - 0x34, 0x53, 0xdd, 0x57, 0x4b, 0x66, 0x37, 0x18, 0x2b, 0x8d, 0x3c, 0x09, 0xb3, 0x6d, 0x07, 0x3d, 0xb4, 0xc0, 0x69, - 0xf7, 0x06, 0x00, 0xc3, 0xb6, 0xa3, 0x28, 0x6d, 0x47, 0x91, 0x9a, 0xd2, 0xcf, 0x8f, 0xaa, 0xed, 0x60, 0x83, 0x88, - 0xf9, 0x95, 0xf4, 0x01, 0xb0, 0x82, 0xc4, 0x2b, 0x86, 0x2a, 0xe6, 0xf5, 0xbc, 0x16, 0xdf, 0x5a, 0x2a, 0x56, 0xc4, - 0x3c, 0x83, 0x43, 0xf1, 0x52, 0x9b, 0x61, 0x42, 0xdd, 0x9e, 0x23, 0x32, 0x34, 0xc9, 0x87, 0x6d, 0x20, 0x7a, 0xe5, - 0x60, 0x4f, 0xcd, 0x63, 0x91, 0x84, 0x55, 0x73, 0xef, 0x08, 0x48, 0x7b, 0x18, 0xbe, 0x16, 0x11, 0xc7, 0x9e, 0xf2, - 0xe6, 0xb3, 0x24, 0x7c, 0xde, 0x08, 0x17, 0x47, 0x18, 0x11, 0x3a, 0xf0, 0x47, 0x8b, 0x1c, 0xf8, 0x01, 0x7f, 0x0d, - 0x9a, 0x41, 0x28, 0x9b, 0xa2, 0xa1, 0x87, 0x21, 0x60, 0x8f, 0x16, 0xde, 0x70, 0x9b, 0x1b, 0xd5, 0xa8, 0x51, 0x92, - 0xf2, 0x42, 0x81, 0xe1, 0x1e, 0x97, 0xa6, 0x3d, 0x32, 0x06, 0x19, 0x31, 0x76, 0x30, 0xe6, 0xef, 0x8f, 0xb0, 0x1a, - 0x27, 0x28, 0xdc, 0x92, 0x4e, 0x5b, 0xc5, 0xfe, 0x0e, 0xfc, 0x14, 0x38, 0x38, 0xd6, 0x81, 0x9d, 0xb5, 0xb5, 0x95, - 0xc8, 0x45, 0xed, 0xa5, 0x3d, 0x8a, 0x44, 0xa0, 0x3f, 0xb8, 0xf0, 0x53, 0xa8, 0x46, 0x14, 0x51, 0x11, 0x69, 0xa0, - 0x66, 0x54, 0xad, 0x82, 0xef, 0xc8, 0xf4, 0xc0, 0x73, 0x74, 0x5b, 0x93, 0xa2, 0xa8, 0x1b, 0x0b, 0x5f, 0xbe, 0xeb, - 0x52, 0x68, 0x0b, 0x03, 0x90, 0x82, 0xd0, 0x04, 0xc1, 0xb8, 0xe4, 0x94, 0xac, 0xe8, 0xef, 0xa3, 0xe1, 0x2b, 0x9f, - 0x1e, 0x65, 0xdb, 0xdb, 0x43, 0x11, 0xb7, 0x20, 0xc2, 0xe1, 0x86, 0x77, 0x35, 0xae, 0x00, 0xa8, 0x4f, 0xe7, 0xc4, - 0x75, 0xc7, 0xb4, 0x22, 0x4d, 0x97, 0x7c, 0x9f, 0x1c, 0x66, 0x00, 0x0c, 0xee, 0x38, 0x47, 0xfe, 0xe0, 0x2f, 0x43, - 0x30, 0x8f, 0xfd, 0xcf, 0xdd, 0x1d, 0xc5, 0x68, 0x7a, 0x32, 0xa6, 0xb8, 0xa4, 0x18, 0x6b, 0xc7, 0x23, 0xdf, 0x68, - 0x90, 0x7b, 0x29, 0xac, 0x00, 0xa4, 0x39, 0xf0, 0x84, 0x8a, 0x82, 0x90, 0xa2, 0x02, 0xdb, 0xc7, 0xc3, 0xcf, 0xf1, - 0x64, 0xbf, 0x03, 0x0d, 0x6f, 0xa0, 0xdf, 0x9e, 0xc2, 0xdb, 0x5f, 0xf4, 0xdb, 0x97, 0x2c, 0xf8, 0xa5, 0x94, 0xae, - 0xfb, 0xda, 0x14, 0x0f, 0xd5, 0x14, 0xa5, 0xd8, 0x22, 0x03, 0x87, 0xcc, 0x5d, 0xf5, 0xd9, 0x70, 0xb7, 0x04, 0x64, - 0x28, 0xd6, 0x05, 0x3a, 0x5a, 0x74, 0x8a, 0xc8, 0x75, 0x4d, 0x54, 0x18, 0xb9, 0x04, 0xe6, 0x82, 0x2b, 0xba, 0x25, - 0xe2, 0xec, 0xb7, 0xdd, 0x65, 0xad, 0x2d, 0xe9, 0x77, 0x6c, 0x36, 0xe7, 0x97, 0x07, 0x24, 0xe8, 0x03, 0x99, 0x36, - 0x20, 0x62, 0xe7, 0xed, 0x5e, 0xbc, 0xc7, 0x7b, 0x31, 0x70, 0xf5, 0x42, 0x91, 0x18, 0x9e, 0x55, 0xef, 0x2d, 0x7a, - 0x29, 0x4d, 0x62, 0xf2, 0x6a, 0xcb, 0xeb, 0xca, 0xe5, 0x6d, 0x6f, 0xc3, 0x02, 0x7b, 0x46, 0x57, 0x2e, 0xba, 0x96, - 0xa5, 0xc0, 0x09, 0x40, 0xf4, 0xb8, 0x4e, 0x72, 0x44, 0x71, 0x98, 0xcd, 0x86, 0x8c, 0x83, 0xb9, 0x6b, 0x47, 0xc5, - 0x31, 0xb1, 0xbb, 0x4c, 0xd8, 0x81, 0x95, 0x11, 0x95, 0xb7, 0x3a, 0xc2, 0x3b, 0x2c, 0xfa, 0x6b, 0xff, 0xf6, 0x47, - 0x8f, 0x6d, 0x77, 0x5c, 0x90, 0x20, 0xb5, 0xb1, 0x1e, 0x55, 0x63, 0x41, 0x7d, 0xf8, 0x51, 0x63, 0xa9, 0xcc, 0xb7, - 0xb7, 0xcb, 0x7a, 0xa8, 0x56, 0x9d, 0xe0, 0x5a, 0x34, 0xe5, 0xa2, 0x99, 0x0d, 0xc2, 0x01, 0x89, 0x09, 0x14, 0x68, - 0x6e, 0x65, 0xc5, 0x00, 0x43, 0xca, 0x72, 0xe4, 0x4f, 0x21, 0xf3, 0xe2, 0xb2, 0xd4, 0xa9, 0x2f, 0xd2, 0x1f, 0x19, - 0x62, 0xd4, 0x93, 0x94, 0x15, 0x10, 0xb0, 0x5e, 0xea, 0x25, 0xb4, 0x45, 0xb0, 0xf2, 0x67, 0x2a, 0x87, 0x46, 0x68, - 0x20, 0x51, 0x68, 0xa8, 0x25, 0x4a, 0xf9, 0xcc, 0xc3, 0x18, 0xa4, 0xfd, 0x93, 0x9a, 0xef, 0x2b, 0x57, 0x4a, 0x47, - 0x7e, 0x54, 0x0c, 0x03, 0xaa, 0x5f, 0x48, 0x0e, 0x36, 0x0d, 0xdf, 0x03, 0x19, 0x55, 0x86, 0x27, 0x31, 0xc2, 0xa7, - 0x71, 0xce, 0xc8, 0x52, 0xd8, 0x94, 0x30, 0x4b, 0xd5, 0x36, 0x52, 0xed, 0x22, 0xd3, 0x09, 0xe5, 0xc2, 0xfc, 0x53, - 0x23, 0x76, 0x91, 0x85, 0x2b, 0xad, 0x41, 0xfd, 0x78, 0x63, 0x02, 0x94, 0x5d, 0x5d, 0x65, 0xc2, 0xc6, 0x8d, 0x48, - 0xdf, 0xd0, 0x15, 0xd3, 0x81, 0x5a, 0x54, 0xe0, 0x44, 0xa4, 0xf1, 0x50, 0x0c, 0x85, 0x46, 0x38, 0xa4, 0x28, 0x72, - 0xe1, 0x1a, 0x87, 0xbe, 0x18, 0x68, 0xdb, 0x28, 0x0d, 0x9d, 0x04, 0x98, 0x80, 0x58, 0xbb, 0xa1, 0x4d, 0xa5, 0x83, - 0x34, 0x48, 0xa8, 0x14, 0xed, 0x1c, 0x58, 0x7f, 0x18, 0x49, 0x0c, 0x80, 0xfe, 0x50, 0x8d, 0x14, 0x51, 0x96, 0x05, - 0x6e, 0x00, 0xcd, 0x75, 0x80, 0x3b, 0xe1, 0x0b, 0x05, 0x15, 0xa6, 0xa7, 0x59, 0x79, 0x29, 0x84, 0xc8, 0xab, 0x35, - 0x29, 0x6b, 0xc4, 0x93, 0xcf, 0xd0, 0xe0, 0x53, 0xd6, 0xf5, 0x6b, 0xb9, 0x0e, 0x5d, 0xf0, 0x14, 0xb6, 0x55, 0x3d, - 0xbf, 0x0a, 0x39, 0x19, 0xd7, 0x20, 0x2b, 0x24, 0xd3, 0x5f, 0x31, 0x92, 0xf7, 0x5f, 0xf9, 0x55, 0x2d, 0x35, 0x86, - 0xb2, 0xf7, 0xeb, 0x9a, 0x61, 0x79, 0x39, 0xaf, 0xdc, 0x14, 0x04, 0xdc, 0x92, 0x25, 0xc1, 0x52, 0x4a, 0x08, 0xd0, - 0xb0, 0x3d, 0x92, 0x4a, 0x41, 0x51, 0x6a, 0xf7, 0xce, 0x53, 0xd0, 0x02, 0x8c, 0xa0, 0x96, 0x4a, 0xa6, 0x91, 0xc8, - 0x97, 0x42, 0x14, 0x88, 0xf2, 0x60, 0x04, 0x76, 0x6a, 0x33, 0xd2, 0x75, 0xe1, 0xfa, 0xf1, 0x0c, 0x53, 0x7b, 0x08, - 0xf4, 0xd8, 0xdb, 0x00, 0x55, 0xa2, 0x2e, 0xc3, 0x72, 0xa2, 0xd0, 0xac, 0x26, 0x59, 0x40, 0x8d, 0x69, 0x83, 0x94, - 0x6c, 0x83, 0x2e, 0x57, 0x80, 0x7e, 0x24, 0x8e, 0x67, 0xb5, 0x03, 0x42, 0xd6, 0xa0, 0x82, 0x21, 0x4f, 0xa9, 0x90, - 0xc2, 0xbc, 0xd7, 0xa5, 0x22, 0x3c, 0x9f, 0x03, 0x2e, 0xb5, 0xe0, 0xcc, 0xcb, 0x68, 0xe0, 0x83, 0xf8, 0x24, 0xc1, - 0xc4, 0x17, 0x5c, 0x15, 0xe8, 0xc1, 0x41, 0xa7, 0xd9, 0x14, 0x28, 0x15, 0x37, 0x29, 0x83, 0x6d, 0x45, 0xae, 0x0d, - 0x3f, 0x24, 0xcb, 0xd6, 0x5d, 0x1e, 0xea, 0x2e, 0x44, 0x02, 0xd8, 0xe9, 0x25, 0x7a, 0xbe, 0x65, 0xbd, 0x74, 0x18, - 0x9c, 0x69, 0x89, 0x83, 0xc0, 0x6f, 0x6f, 0x27, 0xc3, 0x32, 0x25, 0xb2, 0x6b, 0x92, 0xba, 0x80, 0x1c, 0x86, 0x6a, - 0xae, 0x1d, 0x98, 0xa5, 0xd2, 0xc7, 0xf3, 0x72, 0x86, 0xdb, 0xa5, 0x34, 0xe4, 0x66, 0xbc, 0x9a, 0xe6, 0x73, 0x2b, - 0xc9, 0xa6, 0xfd, 0xad, 0xf8, 0xa2, 0xe0, 0x1f, 0x38, 0xb1, 0xd4, 0xea, 0x29, 0xb5, 0xc2, 0xa3, 0xcc, 0x2d, 0x59, - 0xa7, 0xb8, 0x56, 0xd7, 0x0d, 0x54, 0x23, 0x8c, 0xa6, 0x61, 0x23, 0x60, 0x62, 0x82, 0x8a, 0x5f, 0x37, 0x89, 0x98, - 0xce, 0x96, 0xe0, 0x3a, 0x42, 0xef, 0xa1, 0x9c, 0xe0, 0xae, 0xa6, 0xd9, 0xe7, 0xe1, 0xfc, 0x7a, 0xe2, 0xde, 0x37, - 0x88, 0xfb, 0xcb, 0x90, 0x1b, 0x84, 0x1e, 0xcb, 0x84, 0x1f, 0xe9, 0xfb, 0x28, 0x54, 0xd5, 0x93, 0xd3, 0xb0, 0x62, - 0x59, 0xe2, 0xc9, 0x08, 0x75, 0x18, 0x51, 0xd1, 0x1a, 0x23, 0xbb, 0xba, 0xca, 0xcd, 0xb3, 0x40, 0x4e, 0x53, 0x8f, - 0xd7, 0xfd, 0xb4, 0x15, 0x39, 0x1b, 0x9e, 0xc8, 0xfd, 0x57, 0x35, 0x4f, 0x64, 0x45, 0xe7, 0x38, 0xd2, 0x35, 0x81, - 0xdc, 0x27, 0xa7, 0xab, 0x87, 0x54, 0xc8, 0x16, 0xbd, 0x6c, 0xe3, 0x8c, 0xea, 0x80, 0xa4, 0x9e, 0x51, 0x81, 0x55, - 0x8d, 0xbd, 0xb5, 0xd5, 0x11, 0xe9, 0x96, 0x4a, 0xb0, 0xc1, 0xd6, 0xc2, 0x68, 0xc6, 0x28, 0xe8, 0x94, 0x14, 0x19, - 0xa8, 0x51, 0x7e, 0x0d, 0x63, 0xd8, 0xa7, 0x06, 0x20, 0x38, 0xd7, 0x57, 0x7f, 0x59, 0x4a, 0xb2, 0x10, 0x90, 0xb8, - 0x4b, 0x06, 0x6c, 0x4d, 0x10, 0x33, 0xd2, 0xc9, 0x7b, 0xa0, 0xbc, 0x01, 0x43, 0x1b, 0x01, 0xec, 0x02, 0x71, 0xe8, - 0x41, 0xc5, 0xb6, 0x09, 0x29, 0x3a, 0x36, 0xf0, 0x1c, 0x80, 0x9d, 0x57, 0xae, 0xd1, 0x77, 0x55, 0x0a, 0x18, 0x92, - 0x81, 0x1b, 0xb0, 0xca, 0x2d, 0xb7, 0xff, 0x1c, 0xcc, 0x06, 0x78, 0x7d, 0x26, 0x9b, 0x6f, 0x62, 0x9e, 0x60, 0x15, - 0xbb, 0xf0, 0x2b, 0xcd, 0x5a, 0xc4, 0x9d, 0x0e, 0x1b, 0xf5, 0x0a, 0x13, 0xa2, 0xf6, 0x00, 0x6b, 0xdf, 0xa3, 0x87, - 0x45, 0xbc, 0xbf, 0xc2, 0x77, 0x3d, 0x6e, 0xb9, 0xaf, 0x97, 0x45, 0x2b, 0x5d, 0x45, 0x8d, 0x81, 0xc9, 0xba, 0x9d, - 0x8c, 0x6b, 0x2f, 0x0f, 0x84, 0x2f, 0xb8, 0x5a, 0x23, 0xab, 0x5c, 0x8a, 0x8d, 0x45, 0xd2, 0xd3, 0x3e, 0x05, 0xd8, - 0x37, 0x9b, 0xbd, 0x00, 0x33, 0xef, 0x2b, 0x54, 0x49, 0x48, 0x69, 0x76, 0x83, 0x25, 0x09, 0x6d, 0x45, 0x46, 0x9d, - 0x0f, 0x1c, 0x6d, 0x73, 0x2b, 0x8e, 0x60, 0x38, 0x27, 0x61, 0x3a, 0x56, 0x1e, 0x36, 0x19, 0xb8, 0xf2, 0x8e, 0x98, - 0xb6, 0x09, 0xf0, 0x6f, 0x06, 0x7c, 0x7b, 0x25, 0xb9, 0xb6, 0xd0, 0x30, 0x3c, 0x41, 0x84, 0x55, 0x9e, 0x08, 0x34, - 0x14, 0x60, 0x8d, 0x6b, 0x2d, 0x0f, 0x50, 0xe1, 0x6b, 0x67, 0x13, 0x00, 0x12, 0x59, 0x41, 0xce, 0x8a, 0xa3, 0x1b, - 0x56, 0xb9, 0xde, 0x4f, 0x8d, 0x82, 0xc4, 0xc5, 0x83, 0xe9, 0xea, 0x96, 0xfe, 0x0c, 0x35, 0x67, 0x52, 0xc4, 0xb4, - 0x13, 0x04, 0xfd, 0xa3, 0xcc, 0xc9, 0x69, 0x3a, 0xa1, 0x7d, 0xce, 0x9d, 0xda, 0xd4, 0x3d, 0x46, 0xdd, 0x3c, 0x89, - 0x2d, 0x5e, 0xc7, 0x4d, 0x29, 0x17, 0x26, 0x39, 0xe6, 0xa6, 0x48, 0xc5, 0x66, 0x8a, 0xdd, 0xb9, 0xf5, 0x83, 0x16, - 0xd2, 0x41, 0xdb, 0x14, 0x39, 0xd8, 0xac, 0xe2, 0xf7, 0x04, 0xc6, 0x73, 0x81, 0xf8, 0xf2, 0x15, 0x25, 0xe9, 0x30, - 0xc7, 0x5c, 0x60, 0xf5, 0x62, 0x0a, 0xf2, 0x77, 0x8e, 0x4e, 0xb3, 0x37, 0xf0, 0x41, 0xe2, 0x0d, 0x38, 0x66, 0x8d, - 0x7d, 0xe7, 0x52, 0x51, 0x47, 0x08, 0x54, 0x46, 0xb5, 0x4c, 0xc7, 0x89, 0x95, 0xfb, 0x46, 0xd0, 0xd5, 0x5b, 0x1d, - 0xce, 0x37, 0x9e, 0x1b, 0xbb, 0x11, 0xc4, 0x60, 0x2d, 0x14, 0x43, 0x4f, 0xb2, 0xf0, 0x1c, 0xb6, 0x67, 0x7b, 0xbb, - 0x57, 0xec, 0xf1, 0xca, 0x45, 0x52, 0xc1, 0x18, 0x63, 0x46, 0x31, 0x9e, 0x89, 0x9a, 0x58, 0x44, 0x64, 0xcb, 0xd6, - 0x61, 0x81, 0x01, 0x00, 0x68, 0x69, 0x72, 0xaf, 0x9a, 0x08, 0x95, 0xf1, 0x5c, 0x5a, 0x4f, 0x15, 0x44, 0x55, 0x8d, - 0xdf, 0xae, 0xcf, 0x40, 0x21, 0xb8, 0x37, 0x3a, 0x1e, 0x06, 0x21, 0x60, 0x17, 0x05, 0x2f, 0xd0, 0x07, 0xb4, 0x57, - 0x25, 0x42, 0x31, 0x73, 0xb2, 0x1e, 0x33, 0x8c, 0x54, 0xd0, 0x85, 0x4a, 0xd8, 0x2a, 0xcd, 0xf0, 0xab, 0x83, 0xd0, - 0x8c, 0x32, 0xee, 0xbf, 0xaa, 0xd6, 0x0c, 0xf2, 0x83, 0x79, 0xab, 0x84, 0xfa, 0x76, 0x25, 0x22, 0x53, 0x81, 0x89, - 0x87, 0x59, 0x4a, 0xbf, 0x5f, 0xd6, 0x49, 0x3f, 0x2f, 0x97, 0xe7, 0x9c, 0x24, 0x5f, 0xe7, 0x0e, 0x92, 0x4f, 0xba, - 0xfb, 0x95, 0xf0, 0x43, 0x0d, 0xa3, 0x26, 0xfc, 0xea, 0x5b, 0x1a, 0xe6, 0x9e, 0x72, 0x6f, 0xf5, 0xbb, 0xc8, 0x74, - 0x51, 0x9e, 0x83, 0x22, 0xa4, 0x1f, 0xc1, 0x34, 0x34, 0x68, 0x50, 0x24, 0x8b, 0xc5, 0xda, 0x04, 0x71, 0x7d, 0xcc, - 0xa9, 0x76, 0x28, 0x63, 0x8c, 0x68, 0x5a, 0x52, 0x90, 0x24, 0x70, 0x50, 0x7e, 0x03, 0x03, 0x62, 0x12, 0x12, 0xd2, - 0x20, 0x74, 0xd6, 0x66, 0x22, 0x2a, 0x73, 0xf1, 0x76, 0xe5, 0xb2, 0x26, 0x50, 0x84, 0x9e, 0x60, 0xa6, 0x52, 0x2a, - 0x08, 0xa4, 0xca, 0xb7, 0xd1, 0xa9, 0x39, 0x43, 0x73, 0xd7, 0x14, 0x40, 0x5e, 0xdb, 0xf5, 0xa0, 0xc9, 0x7b, 0xf2, - 0xa1, 0xaf, 0x13, 0x23, 0x5e, 0x66, 0xd0, 0x35, 0x1c, 0xfe, 0x1a, 0x2b, 0x29, 0x42, 0x26, 0x7c, 0xaf, 0x60, 0x13, - 0x21, 0x99, 0x82, 0x9e, 0x09, 0xf8, 0x43, 0xbd, 0xb2, 0x97, 0xee, 0xe5, 0x95, 0x49, 0x8b, 0xca, 0x56, 0xa2, 0x66, - 0x2d, 0x8e, 0xe2, 0xed, 0x14, 0xce, 0xb3, 0x47, 0x09, 0x04, 0x24, 0xa9, 0x9c, 0xa4, 0x9a, 0xf7, 0x28, 0x1d, 0x02, - 0x48, 0x70, 0xfa, 0x09, 0x2c, 0xb4, 0x9b, 0x12, 0x13, 0x2c, 0xaa, 0xc6, 0x6e, 0x73, 0x90, 0x9a, 0x73, 0x92, 0x7c, - 0x73, 0x94, 0xda, 0xdb, 0x4a, 0x7b, 0xc6, 0xec, 0x00, 0xdb, 0x76, 0xb7, 0xf3, 0xa3, 0x74, 0xbb, 0x33, 0x34, 0x18, - 0x17, 0x86, 0xff, 0x93, 0x12, 0xd3, 0x40, 0x0a, 0x29, 0x1b, 0x3f, 0xa1, 0x0c, 0xc3, 0xff, 0x96, 0x24, 0x80, 0x07, - 0xb5, 0xdd, 0x58, 0x31, 0xee, 0x15, 0x45, 0xc9, 0x6d, 0x55, 0xed, 0x6a, 0x09, 0x1a, 0xba, 0x91, 0xf4, 0x89, 0x62, - 0x9e, 0x13, 0x00, 0xa3, 0xc8, 0xfc, 0x1d, 0xd3, 0x49, 0xde, 0xbf, 0xac, 0x4d, 0xed, 0xf6, 0x7d, 0x3f, 0xca, 0x4f, - 0x29, 0xa4, 0xa2, 0xb2, 0x39, 0x89, 0xf8, 0x77, 0x05, 0x98, 0xe6, 0xc4, 0x47, 0x7a, 0xae, 0x61, 0x28, 0xc0, 0x57, - 0x3a, 0x94, 0x9a, 0xed, 0xe9, 0x1f, 0x9d, 0xed, 0xbe, 0x44, 0x8a, 0x20, 0x81, 0x06, 0x5e, 0xae, 0x59, 0x2f, 0xac, - 0x32, 0xb8, 0x23, 0xfe, 0x14, 0x7c, 0x5f, 0x5e, 0x07, 0x9f, 0x71, 0xfe, 0x05, 0xa0, 0x55, 0x81, 0x01, 0xe5, 0x83, - 0xa6, 0x62, 0x25, 0xd8, 0x25, 0x0a, 0xcc, 0xca, 0xcf, 0x1f, 0xd7, 0x69, 0xdd, 0xd4, 0x2c, 0xd1, 0x29, 0x3f, 0x77, - 0x0d, 0x33, 0xbe, 0xd7, 0xc8, 0x1f, 0xdf, 0x7f, 0x0e, 0xb2, 0x9d, 0x50, 0xbb, 0xb5, 0x55, 0x6c, 0x90, 0x86, 0x86, - 0xf7, 0xc2, 0xe6, 0xd0, 0x16, 0xf1, 0x52, 0xa8, 0x67, 0x2c, 0xc6, 0xdb, 0x22, 0x54, 0x86, 0x0f, 0x58, 0x30, 0x87, - 0x15, 0x82, 0xc5, 0x4e, 0x65, 0xf2, 0x19, 0x0e, 0x9a, 0x22, 0xd7, 0x42, 0x28, 0x7c, 0x39, 0x88, 0x4a, 0x49, 0x8b, - 0x75, 0xb4, 0x3d, 0x3b, 0x83, 0xe7, 0x97, 0x71, 0x01, 0xd8, 0x81, 0xe5, 0x57, 0x58, 0x16, 0x07, 0xc8, 0xc5, 0x43, - 0x59, 0xeb, 0x15, 0x8d, 0xc7, 0x37, 0x76, 0x61, 0x75, 0x01, 0x3e, 0x8d, 0xd2, 0x71, 0x22, 0x26, 0x31, 0x93, 0x2a, - 0xd7, 0xe4, 0xda, 0xe8, 0x5e, 0x5a, 0xa3, 0x79, 0x2e, 0x38, 0x78, 0x85, 0xe0, 0x06, 0xd3, 0x57, 0xf2, 0x72, 0xbd, - 0x82, 0x82, 0xa1, 0xf6, 0xe6, 0x26, 0x98, 0x2b, 0xf1, 0x98, 0xc1, 0x35, 0xfd, 0x3a, 0x9c, 0x8a, 0x6e, 0x5e, 0xae, - 0x18, 0xfc, 0x3a, 0x67, 0xac, 0x21, 0x00, 0x88, 0x4e, 0x1e, 0x5e, 0x6f, 0x26, 0xbd, 0x52, 0xd2, 0x41, 0x49, 0x84, - 0xf8, 0xae, 0xcc, 0xd7, 0x5d, 0x2a, 0xba, 0x72, 0xd5, 0xbd, 0xaf, 0x19, 0x33, 0x2e, 0x18, 0x3d, 0xe7, 0xb3, 0xa4, - 0x71, 0xed, 0x86, 0xee, 0xea, 0xfc, 0xe8, 0xfd, 0x20, 0xf3, 0x16, 0x66, 0x40, 0x26, 0x20, 0x0a, 0x9e, 0x7b, 0xaf, - 0x8d, 0x88, 0xf2, 0xb7, 0x66, 0x88, 0x57, 0x0e, 0xb3, 0x2e, 0x92, 0xfc, 0xed, 0xe0, 0xdb, 0xe0, 0xfa, 0x96, 0x46, - 0x04, 0xb9, 0xab, 0x22, 0xc8, 0x84, 0xb9, 0x99, 0x3e, 0x70, 0xfb, 0x77, 0x65, 0x08, 0x22, 0x2a, 0xa6, 0x43, 0xe5, - 0xb8, 0x7f, 0xb4, 0x41, 0xa5, 0x42, 0xe2, 0x53, 0x95, 0xbb, 0x72, 0x6d, 0x6a, 0xa8, 0xc7, 0x75, 0x32, 0x0b, 0x4d, - 0xb3, 0x26, 0x97, 0xb2, 0x69, 0x31, 0x32, 0x4d, 0x4e, 0xb5, 0xf9, 0xdd, 0x6b, 0x83, 0x74, 0x0c, 0xd5, 0xc5, 0x5a, - 0x2d, 0x98, 0xdf, 0x95, 0x17, 0xde, 0xf5, 0x62, 0x23, 0x95, 0xa1, 0xa6, 0x3d, 0x8a, 0x3e, 0x8e, 0xdb, 0xcc, 0xe5, - 0x51, 0xfa, 0x67, 0x0d, 0x00, 0xd3, 0x10, 0x16, 0xdd, 0x4d, 0xcb, 0xd8, 0x13, 0xcb, 0xd3, 0x13, 0x19, 0x28, 0x7a, - 0xae, 0xf3, 0x55, 0xab, 0xc4, 0xd2, 0x35, 0x08, 0x76, 0x6f, 0xc8, 0x58, 0x95, 0xb8, 0x5b, 0xad, 0x5f, 0xcd, 0xf3, - 0x79, 0xca, 0x57, 0xf2, 0x7c, 0x6a, 0x1a, 0xdd, 0x46, 0xdb, 0xbd, 0x39, 0x35, 0x54, 0xcc, 0xb5, 0xbe, 0xc9, 0x1f, - 0x98, 0xae, 0x83, 0xae, 0x16, 0x81, 0x66, 0x75, 0xaa, 0x9e, 0x95, 0xe5, 0xac, 0x9e, 0xc9, 0x31, 0x13, 0xb6, 0xa9, - 0x34, 0x87, 0xe8, 0x86, 0xa9, 0x9a, 0xe9, 0xc7, 0xc6, 0xb1, 0x90, 0x6d, 0x9e, 0x5f, 0x8e, 0x73, 0xc0, 0xb4, 0x3c, - 0x5f, 0x26, 0x0c, 0x3f, 0x5e, 0x5d, 0xfd, 0x28, 0xf8, 0x54, 0xd5, 0xd1, 0x5b, 0xbe, 0xd4, 0x3d, 0x83, 0x59, 0xa9, - 0x8c, 0x88, 0x13, 0xb6, 0x7e, 0xf0, 0xe6, 0xe9, 0x15, 0xb0, 0x9c, 0xc0, 0xea, 0x4e, 0x98, 0xd3, 0x18, 0xaa, 0x3a, - 0xc0, 0x3f, 0xac, 0x1f, 0x6c, 0xdd, 0x19, 0xfe, 0x61, 0xf0, 0x43, 0x70, 0x63, 0x63, 0xe3, 0x18, 0xef, 0xd6, 0x12, - 0x41, 0x5e, 0x61, 0x40, 0x1f, 0xaf, 0x3e, 0x0a, 0x5c, 0xae, 0x63, 0xdb, 0x03, 0x87, 0xdc, 0xd6, 0xc0, 0xdf, 0x24, - 0x4f, 0x1a, 0x2d, 0x0a, 0x9e, 0xcd, 0xe4, 0x0c, 0x85, 0xbc, 0xe6, 0xe3, 0xa0, 0xee, 0x08, 0x7f, 0x03, 0xa7, 0x16, - 0x5e, 0x5e, 0x7e, 0x82, 0x3e, 0x60, 0xe9, 0x4a, 0x6e, 0x2a, 0xfc, 0x94, 0xf2, 0x88, 0xae, 0xd6, 0x79, 0x30, 0x52, - 0x5c, 0x4c, 0x51, 0xe8, 0xb8, 0xcb, 0x1b, 0x67, 0x23, 0xa3, 0xbf, 0xc4, 0xab, 0x8b, 0x74, 0xf9, 0x48, 0x64, 0xab, - 0x96, 0xde, 0x2f, 0x3a, 0xba, 0x6d, 0xcf, 0x18, 0x9f, 0x66, 0x63, 0x0a, 0xcc, 0xf8, 0x38, 0x11, 0x5e, 0x9f, 0x18, - 0xeb, 0xbb, 0x45, 0xa0, 0xba, 0x39, 0x36, 0xd9, 0xe1, 0x78, 0xbd, 0xd9, 0xac, 0x71, 0x07, 0x6f, 0x9c, 0x27, 0xce, - 0xb2, 0x44, 0x8f, 0xca, 0x52, 0xc3, 0x03, 0x52, 0x21, 0x6e, 0xde, 0x33, 0x81, 0x71, 0xd9, 0x25, 0x71, 0x6d, 0x37, - 0x10, 0x6b, 0xb1, 0x27, 0x31, 0x4b, 0xc6, 0xb6, 0x07, 0xe5, 0x81, 0xbe, 0x18, 0x4d, 0xb7, 0x80, 0x69, 0x7b, 0xed, - 0xec, 0x3c, 0xb5, 0xbd, 0x6a, 0xaa, 0x00, 0x66, 0xc9, 0xf2, 0xf8, 0x14, 0x49, 0xf7, 0x1b, 0xe8, 0x22, 0x06, 0x8c, - 0x8d, 0x2b, 0x73, 0xee, 0x72, 0xdd, 0x8e, 0xf8, 0x46, 0x13, 0xa9, 0x52, 0x1f, 0x51, 0xdf, 0x61, 0x58, 0xab, 0xab, - 0x0c, 0x24, 0x30, 0x8f, 0xbc, 0x3b, 0xae, 0xa5, 0xa7, 0x63, 0x16, 0x93, 0x2a, 0x7d, 0x4b, 0x5d, 0x8b, 0x6b, 0xba, - 0xbd, 0xe2, 0x01, 0xe8, 0x1f, 0xe8, 0xb7, 0x88, 0x85, 0xbf, 0x9d, 0xd7, 0x52, 0x58, 0x1b, 0x73, 0xe4, 0xe8, 0x6b, - 0x0f, 0x7e, 0x61, 0xd5, 0x9e, 0x81, 0x1a, 0x66, 0xc4, 0x48, 0x7e, 0x33, 0xee, 0x55, 0x4d, 0x1c, 0xb9, 0x0b, 0xc0, - 0xfa, 0x96, 0x74, 0x49, 0x0e, 0xaf, 0x64, 0xb9, 0x2a, 0x86, 0xfc, 0x1b, 0xec, 0xb3, 0xde, 0x9c, 0x80, 0x99, 0x38, - 0xe5, 0x25, 0x26, 0xa6, 0x88, 0xcb, 0xcd, 0xd2, 0xe7, 0x69, 0xda, 0x2c, 0xda, 0xc0, 0x29, 0x8c, 0x04, 0x8e, 0xd8, - 0x37, 0xb6, 0xa1, 0x99, 0xb0, 0x11, 0x13, 0x6a, 0x54, 0x4a, 0x09, 0x1f, 0xc8, 0xad, 0x96, 0xf4, 0x65, 0x6e, 0xaf, - 0xbe, 0xdc, 0x26, 0x28, 0xa0, 0xa8, 0x81, 0xe5, 0xd0, 0x38, 0x6e, 0x19, 0xc8, 0x85, 0xc5, 0xb0, 0x30, 0x6a, 0x55, - 0xae, 0x26, 0xa3, 0x3a, 0x99, 0xaf, 0x16, 0x17, 0x2a, 0xf4, 0xe0, 0x91, 0x40, 0xce, 0x5f, 0x60, 0xea, 0x60, 0x56, - 0x6a, 0x33, 0x2d, 0x36, 0x51, 0xde, 0x33, 0x1d, 0x92, 0xeb, 0xaf, 0xe1, 0xa1, 0xf2, 0x8b, 0x57, 0xe6, 0x14, 0xf3, - 0x45, 0x1e, 0x4b, 0x5b, 0x63, 0x6e, 0xfd, 0xaf, 0xf2, 0x3e, 0xad, 0x04, 0xec, 0x37, 0x60, 0x53, 0xc6, 0x5a, 0x62, - 0xe3, 0x82, 0xa4, 0xbc, 0x96, 0xa7, 0xf4, 0xbe, 0x86, 0xf0, 0x5d, 0x51, 0xe9, 0x2a, 0x91, 0x75, 0x8d, 0x56, 0xf7, - 0xeb, 0x82, 0xe5, 0x97, 0x07, 0x0c, 0x73, 0x93, 0x51, 0x21, 0x5b, 0x51, 0xb3, 0x29, 0xbf, 0xda, 0xbb, 0xf1, 0x2b, - 0x0f, 0x25, 0x05, 0xd5, 0x2a, 0xd9, 0xbc, 0x72, 0xc3, 0x31, 0x6e, 0xdc, 0x70, 0x8c, 0x7b, 0x14, 0x57, 0xae, 0x50, - 0xad, 0xf3, 0xdf, 0x57, 0xdd, 0x4f, 0x74, 0xd6, 0x86, 0xfa, 0xd4, 0x0d, 0xd7, 0xa6, 0xa7, 0xdf, 0xb0, 0x54, 0x23, - 0x4b, 0xe8, 0xa6, 0xa5, 0x62, 0x32, 0x12, 0x25, 0xa6, 0xab, 0x94, 0x47, 0x7d, 0x8d, 0xb8, 0x00, 0x76, 0x43, 0xf9, - 0x8b, 0x7f, 0x0d, 0xcf, 0x8f, 0x03, 0x54, 0xa2, 0x96, 0x93, 0x2c, 0xe5, 0xad, 0x49, 0x34, 0x8b, 0x93, 0xcb, 0x60, - 0x11, 0xb7, 0x66, 0x59, 0x9a, 0x15, 0x73, 0xa0, 0x4a, 0xaf, 0xb8, 0x04, 0x1d, 0x7e, 0xd6, 0x5a, 0xc4, 0xde, 0x73, - 0x96, 0x9c, 0x31, 0x1e, 0x8f, 0x22, 0xcf, 0xde, 0xcf, 0x81, 0x3d, 0x58, 0xaf, 0xa3, 0x3c, 0xcf, 0xce, 0x6d, 0xef, - 0x5d, 0x76, 0x02, 0x44, 0xeb, 0xbd, 0xb9, 0xb8, 0x3c, 0x65, 0xa9, 0xf7, 0xfe, 0x64, 0x91, 0xf2, 0x85, 0x57, 0x44, - 0x69, 0xd1, 0x2a, 0x58, 0x1e, 0x4f, 0x40, 0x4c, 0x24, 0x59, 0xde, 0xc2, 0xfc, 0xe7, 0x19, 0x0b, 0x92, 0xf8, 0x74, - 0xca, 0xad, 0x71, 0x94, 0x7f, 0xea, 0xb5, 0x5a, 0xf3, 0x3c, 0x9e, 0x45, 0xf9, 0x65, 0x8b, 0x5a, 0x04, 0x9f, 0xb5, - 0x77, 0xa3, 0x2f, 0x26, 0xf7, 0x7b, 0x3c, 0x87, 0xbe, 0x31, 0x62, 0x31, 0x00, 0xe6, 0x63, 0xed, 0x3e, 0x68, 0xcf, - 0x8a, 0x0d, 0x11, 0x51, 0x8a, 0x52, 0x5e, 0x1e, 0x7b, 0x1f, 0x41, 0xb7, 0x3d, 0xf6, 0x4f, 0x78, 0xea, 0x81, 0x2d, - 0xc7, 0xb3, 0x74, 0x39, 0x5a, 0xe4, 0x05, 0x0c, 0x30, 0xcf, 0xe2, 0x94, 0xb3, 0xbc, 0x77, 0x92, 0xe5, 0x80, 0xb6, - 0x56, 0x1e, 0x8d, 0xe3, 0x45, 0x11, 0xdc, 0x9f, 0x5f, 0xf4, 0x50, 0x57, 0x38, 0xcd, 0xb3, 0x45, 0x3a, 0x96, 0x73, - 0xc5, 0x29, 0x1c, 0x8c, 0x98, 0x9b, 0x15, 0xf4, 0x25, 0x14, 0x80, 0x2f, 0x65, 0x51, 0xde, 0x3a, 0xc5, 0xce, 0xa8, - 0xe8, 0xb7, 0xc7, 0xec, 0xd4, 0xcb, 0x4f, 0x4f, 0x22, 0xa7, 0xd3, 0x7d, 0xe4, 0xa9, 0x7f, 0xfe, 0x03, 0x17, 0x14, - 0xf7, 0xb5, 0xc5, 0x9d, 0x76, 0xfb, 0x1f, 0xdc, 0x5e, 0x63, 0x16, 0x02, 0x28, 0xe8, 0xcc, 0x2f, 0xac, 0x22, 0x4b, - 0x60, 0x7f, 0xd6, 0xf5, 0xec, 0xcd, 0xc1, 0x6e, 0x8a, 0xd3, 0xd3, 0xa0, 0x3b, 0xbf, 0x28, 0x71, 0x75, 0x81, 0x48, - 0xc8, 0x94, 0x8b, 0x94, 0x6f, 0xcb, 0x3f, 0x0a, 0xf1, 0xe3, 0xf5, 0x10, 0x77, 0x15, 0xc4, 0x15, 0xd6, 0x5b, 0x63, - 0x38, 0x07, 0x84, 0xfe, 0x4e, 0x21, 0x00, 0x99, 0x82, 0x11, 0x98, 0x2b, 0x38, 0xe8, 0xe5, 0x0f, 0x83, 0xd1, 0x5d, - 0x0f, 0xc6, 0xe3, 0xdb, 0xc0, 0xc8, 0xd3, 0xf1, 0xb2, 0xbe, 0xaf, 0x1d, 0x30, 0x4e, 0x7b, 0x53, 0x86, 0xf4, 0x14, - 0x74, 0xf1, 0xf9, 0x3c, 0x1e, 0xf3, 0xa9, 0x78, 0x24, 0x72, 0x3e, 0x17, 0x75, 0x0f, 0xda, 0x6d, 0xf1, 0x5e, 0x80, - 0x40, 0x0b, 0x3a, 0x3e, 0x36, 0x00, 0x22, 0x7a, 0x71, 0xdd, 0x47, 0x6c, 0x3e, 0xdc, 0xfa, 0xa5, 0x1a, 0xef, 0x52, - 0xe5, 0x0d, 0x0a, 0x11, 0xa1, 0xbe, 0xd9, 0x82, 0x19, 0x6f, 0x45, 0xbf, 0xa3, 0x03, 0x55, 0x83, 0x0f, 0x8c, 0xa4, - 0x5e, 0xc0, 0x3d, 0x33, 0x17, 0xa8, 0x97, 0xf6, 0xd1, 0x25, 0xd5, 0x6a, 0xb9, 0x20, 0x37, 0x18, 0xba, 0x90, 0x28, - 0x20, 0xe8, 0x14, 0x83, 0x9c, 0xbe, 0xa9, 0x91, 0xb9, 0x41, 0xee, 0x64, 0x2e, 0x1c, 0xf9, 0x4c, 0xf3, 0xf5, 0x62, - 0x6b, 0x0b, 0xac, 0xec, 0x17, 0x4c, 0x36, 0x00, 0xee, 0x4d, 0xae, 0xae, 0xef, 0x43, 0x61, 0x4a, 0x29, 0x43, 0x6a, - 0x76, 0xd3, 0x15, 0x7d, 0xd8, 0x95, 0x98, 0x32, 0x92, 0x8f, 0x86, 0xff, 0x0e, 0xc5, 0xde, 0xd1, 0x86, 0x65, 0x91, - 0x2d, 0xf2, 0x11, 0x79, 0xea, 0x56, 0x2d, 0x7e, 0x9b, 0x04, 0xae, 0xed, 0x31, 0xcd, 0xe7, 0xd1, 0x0c, 0xae, 0x7d, - 0xe4, 0x80, 0x53, 0x10, 0x44, 0xdc, 0x31, 0x90, 0x5e, 0x0e, 0x05, 0x21, 0x8a, 0xae, 0x31, 0xe5, 0xbb, 0xd1, 0xfd, - 0x4b, 0x7f, 0x91, 0xc6, 0xc0, 0xe9, 0x3e, 0xc6, 0x63, 0xba, 0x77, 0x12, 0x8f, 0x29, 0x10, 0xd1, 0xa2, 0xc4, 0x23, - 0xf4, 0x6c, 0x43, 0x81, 0xfa, 0x0e, 0x0b, 0x3c, 0xcb, 0x44, 0x16, 0xbb, 0x65, 0x63, 0x30, 0xc1, 0x10, 0x95, 0xe3, - 0x6c, 0x16, 0xc5, 0x69, 0x80, 0xdf, 0x07, 0xf1, 0xf4, 0x88, 0x01, 0x76, 0xf1, 0xe0, 0x27, 0x93, 0xb9, 0x68, 0x1d, - 0xd7, 0xff, 0x05, 0xf8, 0x08, 0xf5, 0x2f, 0xa5, 0x1d, 0xa6, 0xe1, 0x52, 0x61, 0xde, 0x7a, 0x29, 0xf0, 0x1e, 0xae, - 0x74, 0x56, 0x46, 0x7e, 0x8e, 0x3d, 0x4e, 0x3f, 0x06, 0xad, 0x4e, 0xd0, 0xd1, 0xa6, 0x6b, 0xed, 0x36, 0xaa, 0xc8, - 0x65, 0x91, 0x37, 0x1a, 0x09, 0x06, 0xfd, 0x2c, 0xe0, 0xac, 0xde, 0x35, 0xac, 0x9e, 0xa4, 0x4b, 0x74, 0xe0, 0x9c, - 0xa6, 0x4e, 0x0d, 0x08, 0x8a, 0x05, 0x5c, 0x33, 0x95, 0x5b, 0x46, 0x24, 0x94, 0xbe, 0xa4, 0x03, 0x5c, 0xbf, 0x4b, - 0x84, 0xf7, 0x86, 0xea, 0x29, 0x50, 0x8a, 0xe4, 0x16, 0xc7, 0x7b, 0xe2, 0xc4, 0x5b, 0x44, 0x63, 0xa1, 0x0d, 0x47, - 0xd0, 0xb6, 0xfe, 0x32, 0x02, 0x2c, 0x7d, 0x0a, 0xed, 0xcd, 0xa5, 0xa3, 0x12, 0xeb, 0x73, 0x98, 0x6b, 0x5f, 0x48, - 0x3d, 0xba, 0x91, 0x6f, 0xf7, 0x37, 0x97, 0xbc, 0xdc, 0xdb, 0x11, 0xbd, 0xfb, 0xc7, 0x65, 0x41, 0x02, 0xca, 0x74, - 0xa4, 0x55, 0x53, 0x88, 0x3a, 0x18, 0x96, 0xd2, 0x77, 0x71, 0xdc, 0x42, 0x2b, 0x5d, 0xc2, 0x63, 0x2c, 0xc9, 0x2e, - 0xc7, 0x74, 0xa5, 0x28, 0x87, 0x33, 0xa9, 0x13, 0x52, 0x72, 0x91, 0x83, 0xd1, 0x5b, 0x85, 0xe2, 0x18, 0x21, 0x18, - 0x6c, 0x2e, 0xe3, 0x32, 0xdc, 0x5c, 0x66, 0xe5, 0x31, 0x68, 0x26, 0x08, 0x55, 0xa1, 0x3e, 0xef, 0x02, 0x13, 0x0b, - 0x27, 0x8b, 0x45, 0x23, 0xe0, 0xb4, 0xac, 0xb4, 0xad, 0x81, 0x80, 0x06, 0x2c, 0x40, 0x2c, 0x00, 0xdd, 0x8d, 0x7a, - 0x31, 0x58, 0x8b, 0x68, 0xdd, 0x87, 0x81, 0xf6, 0x76, 0x44, 0x23, 0x58, 0x57, 0x8e, 0x20, 0x57, 0xcb, 0xc2, 0x74, - 0x1c, 0x73, 0x69, 0x49, 0x74, 0xc2, 0x12, 0xe8, 0x9f, 0x5f, 0x5d, 0xb5, 0xa1, 0x9b, 0x78, 0xb5, 0xf6, 0xe2, 0x74, - 0xbe, 0x90, 0xdf, 0xd4, 0x82, 0x59, 0x3a, 0x18, 0xe6, 0xc4, 0x94, 0xff, 0x81, 0x8a, 0xdb, 0x05, 0x36, 0x8d, 0x6b, - 0x03, 0x3c, 0x14, 0x32, 0x40, 0x50, 0x2a, 0x1a, 0x80, 0xd2, 0x78, 0xbc, 0x5a, 0xa6, 0x97, 0x51, 0xc0, 0x0b, 0x9c, - 0xc1, 0x39, 0x3e, 0xa7, 0xf0, 0x3c, 0x8b, 0x53, 0x7c, 0xcc, 0xf1, 0x31, 0xba, 0xc0, 0xc7, 0xac, 0xb4, 0xff, 0x2e, - 0xe8, 0xb6, 0x34, 0x02, 0xb2, 0xab, 0x2b, 0x60, 0xee, 0x1a, 0x05, 0x40, 0x10, 0xe2, 0xdb, 0x2a, 0xcc, 0xc4, 0x16, - 0x2b, 0xe6, 0x2d, 0x51, 0x6e, 0x91, 0xf0, 0x0c, 0xc1, 0xb6, 0xca, 0x9d, 0x86, 0x8e, 0xe0, 0xc9, 0x2c, 0x92, 0x27, - 0xf8, 0xe2, 0xda, 0x96, 0xf8, 0xf8, 0x85, 0x40, 0x07, 0x3d, 0xe2, 0xda, 0x74, 0x19, 0x97, 0x9f, 0xb5, 0x89, 0x43, - 0x1b, 0x67, 0x01, 0x35, 0x0d, 0x99, 0x3d, 0x8f, 0xe2, 0x44, 0x34, 0x5e, 0xb3, 0x92, 0x46, 0x3a, 0x20, 0x2d, 0x64, - 0x6f, 0xa7, 0x82, 0x0d, 0x80, 0x1f, 0x89, 0xcb, 0xd4, 0x15, 0xf4, 0xb6, 0xa8, 0xa2, 0x28, 0xb9, 0x3c, 0xbc, 0x03, - 0xe1, 0x0f, 0xd7, 0xeb, 0x1c, 0x82, 0x5d, 0x17, 0xa5, 0xf5, 0x16, 0x00, 0xf1, 0x9c, 0xb1, 0xb1, 0x67, 0x5b, 0xc0, - 0x26, 0xc5, 0xf3, 0xc7, 0x84, 0x9d, 0x31, 0xf9, 0x11, 0x14, 0xdd, 0x57, 0x57, 0x8e, 0x40, 0xda, 0x72, 0x79, 0x3f, - 0x53, 0x52, 0x9e, 0x5a, 0x97, 0x5c, 0x7d, 0x1d, 0x78, 0xcf, 0x36, 0x06, 0x6d, 0xce, 0xd1, 0xae, 0x0f, 0xeb, 0x75, - 0x40, 0x91, 0xb5, 0x01, 0x4c, 0xd2, 0xcf, 0x6e, 0x5a, 0x0a, 0xf4, 0x63, 0x93, 0x09, 0x1c, 0x00, 0x15, 0xb7, 0xd0, - 0xa7, 0x5b, 0x00, 0x03, 0x66, 0xa6, 0x67, 0x8b, 0x16, 0x76, 0xd5, 0x56, 0x3f, 0x21, 0x2a, 0x92, 0x6c, 0xf4, 0xa9, - 0x36, 0xc5, 0x02, 0x09, 0x08, 0xc7, 0x6a, 0xf0, 0x29, 0xfb, 0xdf, 0xfe, 0xf5, 0x7f, 0xfe, 0x57, 0x18, 0x8e, 0x3a, - 0xb8, 0xa5, 0x75, 0x7d, 0xab, 0xff, 0x01, 0xad, 0x16, 0xe9, 0x2d, 0xed, 0xfe, 0xf6, 0xcf, 0xff, 0x0d, 0x9a, 0xd1, - 0xcd, 0x1a, 0xb7, 0x3c, 0x0e, 0xec, 0x11, 0x6a, 0x32, 0x77, 0x03, 0xa4, 0xd6, 0xf5, 0xda, 0xf1, 0xff, 0x05, 0x81, - 0x2d, 0x78, 0x36, 0xbf, 0x11, 0x08, 0x84, 0x75, 0x94, 0x64, 0x05, 0x13, 0x50, 0x08, 0x36, 0x79, 0x47, 0x30, 0x68, - 0x86, 0x39, 0x90, 0x6c, 0x61, 0x89, 0xde, 0x02, 0xfb, 0xb5, 0xde, 0x8d, 0x5d, 0x29, 0x18, 0x27, 0xd0, 0xc9, 0x03, - 0x00, 0xfb, 0x20, 0x9e, 0xe0, 0x81, 0x4e, 0x33, 0x6c, 0xbb, 0xce, 0x17, 0x68, 0x0c, 0xa1, 0x89, 0x4c, 0x8c, 0x20, - 0x5c, 0x1d, 0xaa, 0x1f, 0xfc, 0x04, 0xd6, 0xf2, 0x51, 0x3f, 0x47, 0x17, 0xfa, 0x19, 0xd9, 0x0f, 0x0c, 0x0b, 0x82, - 0x62, 0x86, 0x3a, 0x40, 0x73, 0x61, 0xea, 0xa4, 0x56, 0xfc, 0x81, 0xa9, 0xe4, 0xb0, 0x8f, 0x98, 0x0f, 0x89, 0xb7, - 0x5f, 0x16, 0x39, 0xab, 0x38, 0x26, 0x36, 0x10, 0xac, 0xc8, 0xac, 0xff, 0x98, 0x64, 0xe7, 0xe5, 0x75, 0x75, 0x53, - 0xa0, 0xe2, 0x72, 0x6f, 0x1c, 0x9f, 0xf5, 0x25, 0x22, 0x1b, 0x6b, 0x59, 0xed, 0xd2, 0x5c, 0x18, 0x56, 0xc9, 0x75, - 0xc9, 0x47, 0x5c, 0x96, 0xd7, 0x46, 0x01, 0x80, 0xe3, 0xee, 0x9d, 0xe4, 0x7d, 0xb9, 0x80, 0x57, 0x78, 0x61, 0x8b, - 0x20, 0x41, 0x3e, 0x2e, 0x64, 0x0c, 0x27, 0x19, 0x63, 0xb2, 0x7a, 0xd4, 0x5a, 0x33, 0xc5, 0xd2, 0xb1, 0x61, 0x8d, - 0x0b, 0x73, 0xc9, 0x85, 0x63, 0xa9, 0x0e, 0x49, 0x2e, 0x8c, 0x1f, 0xe0, 0x68, 0x70, 0xe1, 0xf8, 0x5a, 0x2e, 0x8c, - 0x6b, 0x1b, 0xe0, 0xc8, 0xa1, 0xbd, 0x8d, 0xb6, 0xb8, 0x21, 0x15, 0x38, 0x0a, 0x37, 0x30, 0xc0, 0x46, 0x9f, 0xa4, - 0x6c, 0x23, 0x50, 0xd1, 0x18, 0x95, 0xd2, 0x9a, 0x04, 0x9b, 0x64, 0xcf, 0xc1, 0xe2, 0x18, 0x64, 0x9b, 0x39, 0x32, - 0x58, 0xc2, 0x13, 0x86, 0xc7, 0xff, 0x78, 0x07, 0xfb, 0x8a, 0xcd, 0x2c, 0xe9, 0x19, 0xa4, 0xcf, 0x0e, 0x0d, 0xe0, - 0x2d, 0x85, 0x3b, 0x23, 0xb0, 0xdf, 0xbe, 0x39, 0x38, 0xb4, 0xbd, 0x93, 0x6c, 0x7c, 0x19, 0xd8, 0xa0, 0x8a, 0x82, - 0x24, 0x73, 0x7d, 0x3e, 0x65, 0xa9, 0xa3, 0x94, 0xc1, 0x2c, 0x01, 0x65, 0x38, 0x3b, 0x15, 0xb7, 0xef, 0x9b, 0xae, - 0x58, 0x40, 0x1b, 0x7d, 0x9e, 0xaf, 0xbf, 0xc7, 0xc5, 0x97, 0x2b, 0x79, 0x8e, 0x8f, 0x7d, 0x0c, 0x46, 0xef, 0xed, - 0xc0, 0x03, 0xbe, 0x1c, 0x20, 0x05, 0xe9, 0x37, 0x01, 0x67, 0x21, 0xde, 0x77, 0xb0, 0xfd, 0x8e, 0xea, 0x8b, 0x50, - 0x28, 0x1a, 0xd0, 0xfa, 0x5a, 0xa5, 0x04, 0xd0, 0xd8, 0x63, 0x22, 0x41, 0xdc, 0x18, 0xc0, 0x01, 0x1f, 0xeb, 0x12, - 0x41, 0xa6, 0x46, 0x11, 0x8d, 0x52, 0xb1, 0x7f, 0x59, 0x85, 0x13, 0x12, 0xfa, 0xc4, 0x64, 0xf0, 0x93, 0xc0, 0x3f, - 0x36, 0xbf, 0x34, 0x25, 0x3e, 0x0a, 0xa3, 0x17, 0x79, 0xf4, 0x57, 0xb0, 0x61, 0xbd, 0xf3, 0x63, 0x6a, 0xa9, 0xcc, - 0x1a, 0xb4, 0xb7, 0xd1, 0xfc, 0x6b, 0x2b, 0xfb, 0x15, 0x24, 0x5e, 0x12, 0xcd, 0x0b, 0x16, 0xa8, 0x07, 0x69, 0xe1, - 0xa0, 0xa1, 0xb4, 0x6a, 0x52, 0x9a, 0x92, 0xb1, 0xe4, 0xd3, 0xa5, 0x69, 0x02, 0x3d, 0x04, 0x13, 0x08, 0xd3, 0xb7, - 0x5b, 0x11, 0xb0, 0xf7, 0x34, 0x48, 0xd8, 0x84, 0x97, 0x1c, 0xef, 0x07, 0x2f, 0x95, 0xcd, 0xe9, 0x77, 0x1f, 0x80, - 0x59, 0x64, 0xf9, 0xf8, 0xff, 0x6d, 0x63, 0x8f, 0x83, 0x14, 0xcc, 0x18, 0xba, 0x30, 0x80, 0x97, 0xb1, 0x00, 0x22, - 0xf3, 0x7d, 0x69, 0x4c, 0x34, 0x62, 0x68, 0x8f, 0x97, 0x3c, 0xb7, 0xf8, 0xd4, 0xe3, 0xb9, 0xd9, 0x0e, 0x34, 0xa5, - 0x15, 0xa3, 0x7c, 0xd5, 0x2c, 0xdc, 0x75, 0xa5, 0xf2, 0xb8, 0xda, 0x58, 0xd9, 0xd6, 0xf5, 0xb7, 0x15, 0x0c, 0x19, - 0x5e, 0x80, 0x52, 0x70, 0xbe, 0xa5, 0xe8, 0x61, 0xae, 0x69, 0xd5, 0x3f, 0x70, 0xab, 0xee, 0x51, 0xd2, 0xd9, 0x3e, - 0xa2, 0xb3, 0x4d, 0xcc, 0x65, 0xb8, 0x14, 0x73, 0x8f, 0xa2, 0x64, 0xe4, 0x20, 0x00, 0x56, 0xcb, 0xba, 0x0f, 0xd8, - 0x04, 0x2e, 0x3d, 0x2c, 0xcb, 0xde, 0x25, 0x73, 0x8e, 0x7e, 0x93, 0x79, 0xe4, 0xe2, 0xfa, 0xa0, 0xfe, 0x04, 0x5b, - 0xbb, 0x74, 0x87, 0xde, 0xf7, 0xc6, 0x77, 0xad, 0x6c, 0x45, 0xa9, 0xb6, 0x07, 0xf8, 0xfd, 0x3e, 0xc4, 0xbe, 0xaf, - 0x1c, 0x1b, 0xb5, 0x10, 0xaa, 0xb9, 0x6c, 0x11, 0xe1, 0xd8, 0xd8, 0x4d, 0x78, 0x41, 0xbf, 0xba, 0xce, 0x98, 0xfd, - 0xee, 0x76, 0x63, 0x96, 0xdd, 0xd1, 0x98, 0xfd, 0xee, 0x4f, 0x36, 0x66, 0xbf, 0x6a, 0x1a, 0xb3, 0xbf, 0xfe, 0x1e, - 0x63, 0x36, 0xcf, 0xce, 0x8b, 0xb0, 0x23, 0x83, 0xa7, 0xc0, 0x4c, 0xfe, 0x3e, 0x56, 0x2d, 0x4c, 0xd4, 0xb0, 0x69, - 0xc9, 0x88, 0x15, 0xf9, 0x5e, 0xc0, 0xab, 0xa5, 0x09, 0xd9, 0xd6, 0x89, 0x55, 0xad, 0xfb, 0xea, 0x26, 0x09, 0xe8, - 0xf5, 0xae, 0xbe, 0x03, 0xd5, 0x55, 0x46, 0x66, 0x40, 0x9f, 0x82, 0xd4, 0x1d, 0xbb, 0xdb, 0x2a, 0xa3, 0xc7, 0x1c, - 0xa1, 0xa7, 0x1c, 0xb5, 0x82, 0x7c, 0x96, 0xf6, 0x7f, 0x3a, 0xea, 0xf4, 0x76, 0x3b, 0x33, 0xe8, 0x0d, 0x72, 0x0b, - 0xde, 0xda, 0xbd, 0xdd, 0x5d, 0x7c, 0x3b, 0x57, 0x6f, 0x5d, 0x7c, 0x8b, 0xd5, 0xdb, 0x03, 0x7c, 0x1b, 0xa9, 0xb7, - 0x87, 0xf8, 0x36, 0x56, 0x6f, 0x8f, 0xf0, 0xed, 0xcc, 0x2e, 0x8f, 0xb8, 0x06, 0xee, 0x11, 0xd0, 0x15, 0x29, 0x89, - 0x81, 0x2a, 0x83, 0xd3, 0x88, 0x37, 0xb0, 0xa2, 0xd3, 0x20, 0xf6, 0x84, 0x02, 0x1d, 0x14, 0xde, 0x39, 0xb0, 0xf4, - 0x80, 0x12, 0x8e, 0x9e, 0xe2, 0x55, 0x7c, 0xd0, 0x3d, 0x0f, 0xe3, 0x19, 0x53, 0xdf, 0x24, 0x55, 0xab, 0x06, 0x35, - 0x05, 0xec, 0xed, 0xb2, 0xa7, 0xf7, 0x49, 0xd8, 0xd0, 0x2a, 0x77, 0x82, 0x76, 0xae, 0xaa, 0x13, 0xd3, 0xb5, 0xf4, - 0x0e, 0x5f, 0x23, 0x20, 0x40, 0x00, 0x2b, 0xa3, 0x74, 0x02, 0x6a, 0x40, 0xeb, 0x02, 0x94, 0xf4, 0xb5, 0x42, 0x03, - 0x21, 0xd2, 0x62, 0x82, 0xd6, 0xa4, 0xdf, 0x0e, 0xa3, 0x53, 0xfd, 0xfc, 0x0a, 0xf4, 0xa9, 0xe8, 0x94, 0xdd, 0x26, - 0x40, 0x08, 0x44, 0x53, 0x78, 0x28, 0x20, 0x48, 0x0b, 0x81, 0xad, 0x41, 0x63, 0x41, 0x0a, 0x0f, 0xc4, 0x4e, 0x5d, - 0x9c, 0xd0, 0xf4, 0xf5, 0x22, 0xc0, 0x68, 0x55, 0xb0, 0x07, 0x6a, 0x1d, 0x95, 0x0a, 0x0c, 0x43, 0x05, 0x16, 0xdc, - 0x28, 0x63, 0x84, 0x2a, 0x72, 0x93, 0xa4, 0xb1, 0x94, 0x90, 0x31, 0x1d, 0xbc, 0xda, 0xbb, 0xbb, 0xca, 0xf7, 0x3e, - 0xeb, 0x8c, 0xf0, 0x8f, 0xe4, 0xaa, 0x9f, 0x4d, 0x26, 0x93, 0x1b, 0x85, 0xce, 0x67, 0xe3, 0x09, 0xeb, 0xb2, 0x07, - 0x3d, 0x74, 0xfe, 0xb5, 0xa4, 0x2f, 0xae, 0x53, 0x12, 0xee, 0x96, 0x77, 0x6b, 0x8c, 0xce, 0x38, 0x90, 0x43, 0x77, - 0x97, 0x4e, 0x25, 0x60, 0x65, 0x09, 0x5c, 0xf9, 0x34, 0x4e, 0x83, 0x76, 0xe9, 0x9f, 0x49, 0x76, 0xfe, 0xd9, 0xe3, - 0xc7, 0x8f, 0x4b, 0x7f, 0xac, 0xde, 0xda, 0xe3, 0x71, 0xe9, 0x8f, 0x96, 0x7a, 0x19, 0xed, 0xf6, 0x64, 0x52, 0xfa, - 0xb1, 0x2a, 0xd8, 0xed, 0x8e, 0xc6, 0xbb, 0xdd, 0xd2, 0x3f, 0x37, 0x5a, 0x94, 0x3e, 0x93, 0x6f, 0x39, 0x1b, 0xd7, - 0x3c, 0x88, 0x8f, 0xc0, 0x78, 0xf5, 0x05, 0xa1, 0x2d, 0xd1, 0x64, 0x10, 0x8f, 0x41, 0xb4, 0xe0, 0x60, 0xeb, 0x02, - 0x6f, 0x67, 0xc0, 0x9f, 0x27, 0x92, 0xb7, 0x8b, 0x4f, 0x7e, 0x22, 0x47, 0xff, 0xd5, 0xe4, 0xe8, 0x48, 0xcc, 0xc4, - 0xcd, 0x19, 0xc9, 0x81, 0x66, 0x35, 0x52, 0x16, 0x55, 0xff, 0x1a, 0xb2, 0x8a, 0xd9, 0x23, 0xb7, 0xc1, 0x96, 0x82, - 0xc7, 0x7f, 0x7d, 0x1d, 0x8f, 0xff, 0xe6, 0x76, 0x1e, 0x7f, 0x72, 0x37, 0x16, 0xff, 0xcd, 0x9f, 0xcc, 0xe2, 0xbf, - 0x6e, 0xb2, 0xf8, 0xcd, 0x3b, 0xb1, 0xf8, 0x35, 0x89, 0x1f, 0xa4, 0x9a, 0xbe, 0x49, 0x43, 0xfb, 0x0d, 0xd8, 0x30, - 0x46, 0xc9, 0x64, 0x02, 0x45, 0x93, 0x89, 0xad, 0x92, 0x1d, 0x81, 0x13, 0x51, 0xab, 0xd7, 0xb5, 0x12, 0x6a, 0xf5, - 0xd5, 0x57, 0x66, 0x99, 0x59, 0x20, 0xfd, 0x0d, 0xa6, 0x7c, 0x57, 0x35, 0x52, 0x65, 0x56, 0x9f, 0x06, 0x19, 0xc7, - 0x05, 0x9e, 0x26, 0x2c, 0x28, 0x79, 0x76, 0x7a, 0x9a, 0x30, 0xfd, 0xed, 0x33, 0xd5, 0xd2, 0x7c, 0x33, 0xe7, 0x33, - 0xcb, 0x07, 0x26, 0xb4, 0x41, 0x0d, 0xd0, 0x9e, 0x70, 0x64, 0xd2, 0xe7, 0xa0, 0x45, 0xd8, 0xfa, 0x4c, 0x7e, 0x37, - 0x98, 0xfc, 0xa9, 0x4b, 0xc9, 0x7e, 0x65, 0x40, 0xb3, 0xea, 0x8a, 0x2e, 0x4c, 0x91, 0x02, 0x32, 0x2e, 0x95, 0xdb, - 0x12, 0xa0, 0x9d, 0xe3, 0x47, 0x4e, 0x74, 0xca, 0xd2, 0xca, 0x37, 0x85, 0x34, 0x9b, 0xc0, 0x8f, 0x1e, 0x88, 0x29, - 0xc4, 0x67, 0x02, 0xf5, 0xb8, 0x22, 0x0e, 0xe8, 0xd4, 0xd6, 0x68, 0xac, 0x2a, 0x0c, 0xcd, 0xa5, 0xa8, 0x9c, 0x93, - 0xd5, 0x79, 0xd6, 0x8a, 0xe6, 0xeb, 0x85, 0xf2, 0xdd, 0xa6, 0xbb, 0x45, 0x34, 0x14, 0xe7, 0x76, 0x5f, 0xdb, 0x98, - 0x35, 0x9a, 0x29, 0xeb, 0x5e, 0x38, 0x9a, 0xe8, 0x24, 0xbb, 0xa8, 0xdb, 0x48, 0x26, 0x0c, 0x68, 0x3e, 0xe9, 0xbd, - 0x57, 0x75, 0xaa, 0xa0, 0x34, 0xbd, 0xa2, 0x22, 0xd3, 0x8b, 0x48, 0x83, 0x7c, 0x60, 0xb0, 0x03, 0xa9, 0x60, 0xca, - 0x30, 0x0f, 0x71, 0x17, 0x6d, 0x47, 0xa0, 0x32, 0x6d, 0x2b, 0x60, 0x51, 0x3a, 0xe4, 0xe8, 0x6b, 0xc2, 0x0e, 0x7d, - 0xab, 0x06, 0x70, 0xaa, 0x6d, 0xb3, 0xdb, 0x19, 0x3e, 0x98, 0x16, 0xe7, 0xc7, 0x7e, 0x71, 0xee, 0xc1, 0x3f, 0xeb, - 0xf3, 0x25, 0xb0, 0xb0, 0x93, 0x4f, 0x31, 0x07, 0x85, 0x71, 0xde, 0x42, 0xa3, 0x98, 0xdc, 0x3b, 0x92, 0xd7, 0x53, - 0xa8, 0x45, 0x5c, 0x89, 0xe8, 0x2d, 0x0a, 0xb4, 0x40, 0x48, 0xd5, 0x0e, 0xd2, 0x2c, 0x65, 0xbd, 0x7a, 0x48, 0xcd, - 0xd4, 0x76, 0x15, 0xb6, 0x86, 0xcb, 0x0c, 0x2d, 0x16, 0x7e, 0x09, 0x16, 0x8b, 0x90, 0x11, 0x6d, 0x15, 0x8e, 0x69, - 0xaf, 0x6d, 0x1f, 0x48, 0x64, 0x6e, 0x93, 0x28, 0xcc, 0x57, 0x55, 0xfa, 0xeb, 0x54, 0xf2, 0xdb, 0x02, 0x4c, 0xdd, - 0x07, 0x0f, 0x3c, 0xf5, 0xcf, 0x88, 0xcc, 0x35, 0x8b, 0x29, 0xc0, 0x74, 0x17, 0xc8, 0x82, 0x68, 0x82, 0x5f, 0x10, - 0xbb, 0x4b, 0xcb, 0x13, 0xca, 0xee, 0x5a, 0xa2, 0xcc, 0x0a, 0x3a, 0x8f, 0xc1, 0xc6, 0xb8, 0xf3, 0xf0, 0x37, 0x2f, - 0xbf, 0x94, 0x38, 0x52, 0x97, 0xf4, 0x6c, 0xbb, 0x87, 0xa7, 0x39, 0x89, 0x2e, 0xc1, 0xd4, 0x21, 0x01, 0x7a, 0x82, - 0xce, 0xae, 0xde, 0x3c, 0x93, 0x91, 0xd2, 0x9c, 0x25, 0xf4, 0x99, 0x7e, 0xb9, 0x15, 0xbb, 0x0f, 0xe7, 0x17, 0x6a, - 0x37, 0x3a, 0x8d, 0x08, 0xe8, 0x9f, 0x1a, 0xe8, 0xbc, 0x3e, 0xb2, 0x5a, 0x0f, 0xd6, 0x3d, 0x00, 0x18, 0x84, 0xd4, - 0x6e, 0xe5, 0x02, 0xaa, 0x36, 0x94, 0x18, 0xa1, 0xde, 0x6a, 0x20, 0xcb, 0xdf, 0x05, 0x09, 0x11, 0x81, 0xbd, 0x8b, - 0x9f, 0x72, 0x8b, 0xc1, 0xa0, 0x92, 0x9a, 0xc1, 0x2c, 0x1e, 0x8f, 0x13, 0xd6, 0x53, 0xc2, 0xdf, 0xea, 0x3c, 0xc4, - 0x48, 0xa9, 0xb9, 0x65, 0xf5, 0x5d, 0x31, 0x90, 0xa7, 0xf1, 0x14, 0x9d, 0x80, 0x32, 0x82, 0xdf, 0x63, 0x5b, 0x8b, - 0x4e, 0x19, 0x42, 0x6c, 0x57, 0xc8, 0xa3, 0xe7, 0xfa, 0x5a, 0x1e, 0x80, 0x26, 0x44, 0x1b, 0x0e, 0x46, 0x75, 0x36, - 0x0f, 0x5a, 0xbb, 0xf5, 0x85, 0x60, 0x95, 0x5e, 0x82, 0xb7, 0x66, 0x59, 0x1e, 0xd0, 0x44, 0x4b, 0x7c, 0xf8, 0xc7, - 0xf2, 0x3b, 0xb2, 0x8c, 0x06, 0xc0, 0x6f, 0x7e, 0xe9, 0xa2, 0xb2, 0xbe, 0x98, 0xff, 0x3f, 0xa7, 0xe5, 0x8b, 0xf5, - 0xa7, 0xe5, 0x0b, 0x75, 0x5a, 0x6e, 0xa6, 0xd8, 0xcf, 0x26, 0x1d, 0xfc, 0xd3, 0xab, 0x16, 0x04, 0xbb, 0x02, 0xe8, - 0xb0, 0x50, 0xe9, 0x6b, 0x75, 0xe1, 0x3f, 0x1a, 0xba, 0xed, 0xe1, 0x1f, 0x1f, 0xd4, 0x9b, 0xb6, 0x85, 0x85, 0xf8, - 0xaf, 0x5d, 0xab, 0xea, 0xdc, 0xc7, 0x3a, 0xec, 0xf5, 0x60, 0xb5, 0xae, 0x7b, 0xf3, 0xa1, 0x05, 0x7e, 0xc5, 0x9d, - 0x40, 0x31, 0x63, 0xb0, 0x43, 0xa2, 0x93, 0x13, 0x28, 0x9d, 0x64, 0xa3, 0x45, 0xf1, 0x8f, 0x12, 0x7e, 0x89, 0xc4, - 0x1b, 0x8f, 0x74, 0x63, 0x1c, 0xd5, 0x55, 0x84, 0xdd, 0xd5, 0x08, 0x4b, 0xbd, 0x4f, 0x41, 0x01, 0x84, 0xc9, 0x9c, - 0xae, 0x7f, 0x7f, 0xcd, 0x21, 0xf8, 0xbb, 0xec, 0xcd, 0xda, 0xc5, 0xfc, 0x7b, 0x91, 0x71, 0x23, 0x12, 0x7e, 0x17, - 0x0e, 0xcc, 0x3d, 0x6c, 0x3f, 0x5e, 0x0f, 0xee, 0x91, 0x9a, 0x69, 0xa8, 0x84, 0x82, 0x94, 0x3b, 0xa0, 0xe2, 0x46, - 0x8b, 0x84, 0xdf, 0x3c, 0xea, 0x75, 0x94, 0xb1, 0x32, 0xea, 0x0d, 0x0c, 0xbd, 0x6a, 0x7b, 0x47, 0x2e, 0xfd, 0xd9, - 0x17, 0xf7, 0xf1, 0x8f, 0xf0, 0xea, 0x9c, 0x54, 0x8a, 0xbf, 0x30, 0x7c, 0x51, 0xf1, 0xdf, 0xac, 0x69, 0xf6, 0x42, - 0x82, 0x93, 0x72, 0x7f, 0xd7, 0xd6, 0xa8, 0xcf, 0xde, 0xa9, 0xb9, 0xd4, 0x83, 0x7e, 0x57, 0xeb, 0xdf, 0x37, 0xf8, - 0x1d, 0xdb, 0x8e, 0x84, 0xce, 0x5c, 0x6f, 0x2b, 0x7f, 0x65, 0xc2, 0x6a, 0x63, 0x81, 0xe7, 0xbb, 0x36, 0x57, 0x1b, - 0x44, 0xed, 0x37, 0xc3, 0x13, 0x6d, 0x1e, 0xc9, 0xb0, 0x1b, 0xb6, 0x17, 0x16, 0xd2, 0xb7, 0x2c, 0xbc, 0x87, 0x9f, - 0x1a, 0xb2, 0x2e, 0x66, 0x49, 0x0a, 0x3a, 0xd5, 0x94, 0xf3, 0x79, 0xb0, 0xb3, 0x73, 0x7e, 0x7e, 0xee, 0x9f, 0xef, - 0xfa, 0x59, 0x7e, 0xba, 0xd3, 0x6d, 0xb7, 0xdb, 0xf8, 0x85, 0x18, 0xdb, 0x3a, 0x8b, 0xd9, 0xf9, 0x97, 0xd9, 0x45, - 0x68, 0x3f, 0xb2, 0x1e, 0x5b, 0x8f, 0x76, 0xad, 0x07, 0x0f, 0x6d, 0x8b, 0xb8, 0x3f, 0x94, 0xec, 0xda, 0x96, 0xe0, - 0xfe, 0xa1, 0x0d, 0xc5, 0xfd, 0xbd, 0x53, 0xa5, 0xc0, 0x61, 0x06, 0xae, 0x50, 0x8f, 0xc0, 0x66, 0xc9, 0x3e, 0xb1, - 0xfa, 0x39, 0x17, 0x65, 0x2d, 0x29, 0x43, 0xd4, 0x2b, 0x1e, 0xf6, 0x51, 0x34, 0x0f, 0x88, 0x86, 0xcc, 0x42, 0x74, - 0x00, 0x89, 0x52, 0x9a, 0x02, 0xa3, 0xba, 0x27, 0xf0, 0x04, 0x1a, 0xfb, 0xd4, 0x82, 0xe7, 0x57, 0xdd, 0x47, 0x20, - 0xe0, 0xce, 0x5a, 0xf7, 0x47, 0xed, 0x56, 0xc7, 0xea, 0xb4, 0xba, 0xfe, 0x23, 0xab, 0x2b, 0xfe, 0x07, 0x06, 0xb9, - 0x6b, 0x75, 0xe0, 0x69, 0xd7, 0x82, 0xf7, 0xb3, 0xfb, 0x22, 0x24, 0x1c, 0xd9, 0x3b, 0xfd, 0x3d, 0xfc, 0x85, 0x29, - 0xb0, 0xa8, 0x2f, 0x6c, 0xf1, 0x2b, 0x9e, 0xec, 0xcf, 0xcc, 0xd2, 0xce, 0xe3, 0xb5, 0xc5, 0xdd, 0x47, 0x6b, 0x8b, - 0x77, 0x1f, 0xae, 0x2d, 0xbe, 0xff, 0xa0, 0x5e, 0xbc, 0x73, 0x2a, 0xaa, 0x34, 0x53, 0x08, 0xed, 0x59, 0x04, 0x54, - 0x72, 0xe1, 0x74, 0x00, 0xce, 0xb6, 0xd5, 0xc2, 0x1f, 0x8f, 0xba, 0xae, 0xee, 0x75, 0x82, 0xbd, 0xf4, 0x2a, 0x1f, - 0x3d, 0x86, 0x55, 0x3e, 0xef, 0x3e, 0x1c, 0x61, 0x3b, 0x5a, 0x28, 0xfc, 0x3b, 0xdb, 0x7d, 0x3c, 0x02, 0x71, 0x60, - 0xe1, 0x3f, 0xf8, 0x33, 0x7d, 0xd0, 0x1d, 0x89, 0x97, 0x36, 0xd6, 0x7f, 0xe8, 0x3c, 0x2a, 0xa0, 0x29, 0xfe, 0xf9, - 0x4d, 0xeb, 0xcf, 0xa8, 0xbe, 0x9b, 0xe3, 0xde, 0x07, 0x1c, 0x3d, 0x9e, 0x76, 0xfd, 0x2f, 0xce, 0x1e, 0xf9, 0x8f, - 0xa7, 0x9d, 0x47, 0x1f, 0xc4, 0x5b, 0x02, 0x18, 0xfc, 0x02, 0xff, 0x7d, 0xd8, 0x6d, 0x83, 0x69, 0xeb, 0x3f, 0x3e, - 0xdb, 0xf5, 0x77, 0x93, 0xd6, 0x43, 0xff, 0x31, 0xfe, 0xab, 0x86, 0x9b, 0x66, 0x33, 0x66, 0x5b, 0xb8, 0xdf, 0x0d, - 0xbb, 0xd0, 0x9c, 0xa3, 0x7b, 0xdf, 0x7a, 0x70, 0xff, 0xf9, 0x63, 0xd8, 0xa3, 0x69, 0xa7, 0x0b, 0xff, 0x5f, 0xf7, - 0xf8, 0x01, 0x11, 0x2f, 0x07, 0x8e, 0x18, 0xe6, 0xce, 0x29, 0xc4, 0xd1, 0xd7, 0x8a, 0xee, 0x79, 0x3f, 0x5e, 0x67, - 0xda, 0xff, 0x70, 0xbb, 0x69, 0xff, 0xd7, 0x3b, 0xba, 0x6f, 0x7f, 0xf8, 0x93, 0x6d, 0xfb, 0x1f, 0x9b, 0xb6, 0xfd, - 0x39, 0x5b, 0x31, 0xee, 0x9b, 0xf6, 0xd9, 0x21, 0x73, 0x8e, 0xbe, 0x65, 0x43, 0xcc, 0x13, 0x85, 0xd6, 0x7f, 0xad, - 0x79, 0x3a, 0x32, 0x3c, 0xc8, 0xe7, 0x4c, 0x9c, 0xe4, 0xef, 0xaf, 0x43, 0x08, 0xe3, 0xb7, 0x22, 0xe4, 0xc5, 0xdd, - 0xf0, 0x41, 0x9f, 0x16, 0xff, 0x13, 0xf1, 0xf1, 0xbd, 0x89, 0x8f, 0x9a, 0x2f, 0x99, 0x8c, 0x79, 0xb2, 0xc1, 0x0f, - 0xe8, 0xdd, 0xb1, 0x77, 0x18, 0xbe, 0x15, 0xb6, 0x48, 0x7e, 0x7a, 0xf7, 0x7b, 0xfc, 0xde, 0x22, 0x8d, 0x32, 0xb4, - 0xa5, 0x83, 0x62, 0x8e, 0x1f, 0xe3, 0x54, 0x2f, 0x67, 0x22, 0x55, 0x3f, 0xa4, 0x7b, 0x36, 0xf7, 0xb5, 0x73, 0x03, - 0x33, 0x5b, 0xc3, 0x65, 0xc6, 0x23, 0xfc, 0x6d, 0x2f, 0x3c, 0xe6, 0x09, 0xde, 0x02, 0x94, 0x37, 0x66, 0x30, 0x11, - 0xf3, 0x5b, 0x4c, 0x22, 0x55, 0xee, 0xef, 0x19, 0x3a, 0x0c, 0x5e, 0xb1, 0x71, 0x1c, 0x39, 0xb6, 0x33, 0x87, 0x13, - 0x0b, 0x63, 0xb6, 0x6a, 0x19, 0x9c, 0x94, 0xbc, 0xe9, 0xda, 0xea, 0x17, 0x8c, 0xe4, 0xf8, 0xc1, 0xa6, 0xf0, 0x48, - 0xba, 0xce, 0x6c, 0xa9, 0xfe, 0xc3, 0xf8, 0xaa, 0x24, 0x47, 0xd6, 0x5d, 0xa9, 0x0c, 0xb6, 0xd0, 0x19, 0x3a, 0x7e, - 0x17, 0x6c, 0x08, 0x2a, 0xc6, 0x0f, 0xe0, 0xfc, 0xe0, 0xb4, 0x76, 0x41, 0xa7, 0x31, 0xba, 0xe9, 0x81, 0x86, 0x2b, - 0x1f, 0xdf, 0x14, 0x7e, 0x83, 0x46, 0xa9, 0xa7, 0x7f, 0xe3, 0x12, 0x50, 0x86, 0xca, 0xf5, 0xff, 0xf2, 0xf2, 0x50, - 0x5e, 0x72, 0xb5, 0xd1, 0x27, 0x49, 0xbe, 0xe8, 0xea, 0x03, 0x3b, 0xdb, 0x20, 0x2e, 0xe8, 0xd7, 0xde, 0x51, 0x50, - 0x16, 0x25, 0x02, 0xe6, 0x98, 0x5a, 0xd2, 0x6c, 0x08, 0x6d, 0x21, 0x0f, 0xc6, 0xec, 0x2c, 0x1e, 0x49, 0xb6, 0xee, - 0x59, 0x32, 0x37, 0xbe, 0x45, 0xab, 0x08, 0x3b, 0x9e, 0x30, 0x9c, 0xe1, 0x05, 0x65, 0x54, 0x98, 0x66, 0x76, 0xff, - 0x5e, 0x4f, 0x43, 0x52, 0x4f, 0xcf, 0xb5, 0xf1, 0x77, 0xf0, 0x1d, 0x81, 0xa1, 0xf6, 0x8f, 0xe1, 0x3d, 0xfc, 0x2d, - 0x7c, 0xf7, 0x86, 0xb6, 0xeb, 0x13, 0x53, 0xbc, 0x57, 0xfd, 0x2a, 0x3e, 0xe4, 0x08, 0xdb, 0x20, 0xbf, 0xbc, 0xbb, - 0x0a, 0x32, 0x29, 0xb4, 0xba, 0x0f, 0x2a, 0xa1, 0x05, 0xcf, 0x06, 0x97, 0x02, 0x06, 0xda, 0xf5, 0x1f, 0x18, 0xac, - 0xf0, 0xac, 0x85, 0x3f, 0x6b, 0xcc, 0xf0, 0x3e, 0x34, 0x50, 0xdc, 0xf0, 0x25, 0x34, 0xdf, 0x15, 0x8c, 0x17, 0xfa, - 0xfd, 0x48, 0xac, 0x4a, 0xb0, 0xa9, 0x3a, 0xc5, 0xac, 0x09, 0x8f, 0x88, 0x78, 0xb6, 0xed, 0x39, 0xfa, 0xfb, 0xfe, - 0x92, 0x5c, 0xe5, 0xe5, 0xa4, 0xa7, 0xd0, 0xd7, 0xd1, 0xdf, 0xad, 0x5d, 0x57, 0xe7, 0xd5, 0x4e, 0xce, 0x9a, 0x29, - 0x90, 0xe0, 0x1b, 0x21, 0x18, 0xca, 0xd5, 0x16, 0xdf, 0x6f, 0x12, 0xc7, 0xb8, 0xfa, 0xc2, 0xd5, 0x9a, 0x74, 0x43, - 0xf3, 0x50, 0xb0, 0x8a, 0x68, 0xe8, 0x5c, 0x00, 0x23, 0xa0, 0x9f, 0x55, 0xb1, 0x7a, 0x90, 0x04, 0xe5, 0x27, 0x11, - 0xfe, 0xfa, 0x09, 0xfa, 0x51, 0x56, 0x07, 0x90, 0xd3, 0x07, 0xfa, 0x08, 0xd2, 0x17, 0xe3, 0xb2, 0xb9, 0x08, 0xd0, - 0x17, 0xf0, 0xb7, 0x99, 0x55, 0xb9, 0xe1, 0xf2, 0xd2, 0x17, 0x86, 0xc1, 0xc7, 0x71, 0x4e, 0x77, 0x09, 0xd5, 0xfa, - 0x6b, 0xd7, 0xfc, 0x2a, 0x54, 0xd3, 0xa9, 0x64, 0xc5, 0xc0, 0xc6, 0x22, 0x5b, 0x65, 0xe9, 0x98, 0x5f, 0xa8, 0x35, - 0x2f, 0x7b, 0x8d, 0x45, 0x9a, 0x0e, 0x7e, 0xc1, 0xdb, 0x16, 0x48, 0xb6, 0x81, 0x8d, 0x5d, 0xbb, 0x26, 0x52, 0x6e, - 0xf0, 0x8e, 0x54, 0xf5, 0x2b, 0x59, 0xcc, 0x03, 0x6f, 0x9b, 0xbb, 0xa5, 0xc7, 0xa5, 0x7d, 0x70, 0xa5, 0xa7, 0xf0, - 0x84, 0x45, 0xdc, 0x8f, 0x52, 0xca, 0xf7, 0x70, 0x0c, 0xb6, 0xe0, 0x75, 0xd8, 0xae, 0x5b, 0x02, 0xe7, 0x31, 0x7e, - 0x67, 0x8d, 0x40, 0xbd, 0x0f, 0x85, 0x6e, 0xe5, 0xb5, 0x9b, 0x76, 0xfb, 0x6f, 0x0e, 0xf7, 0x2d, 0x71, 0x9a, 0xf7, - 0x76, 0xe0, 0x75, 0x8f, 0x6c, 0x61, 0x91, 0x52, 0x10, 0x8a, 0x94, 0x02, 0x4b, 0x64, 0xc3, 0x84, 0xf6, 0x8e, 0x58, - 0xa6, 0x6d, 0xb1, 0x74, 0x24, 0x3c, 0x78, 0x33, 0xb0, 0x15, 0x62, 0xfc, 0x8a, 0xd1, 0x0e, 0x76, 0x6b, 0xe1, 0x4e, - 0xc3, 0x11, 0x10, 0x3e, 0x3e, 0xa5, 0x20, 0xf0, 0xd4, 0x96, 0xfe, 0x3e, 0x10, 0xeb, 0x4c, 0x65, 0x62, 0xc8, 0xa1, - 0x74, 0x5e, 0xde, 0x6a, 0xeb, 0x62, 0x71, 0x32, 0x03, 0x3e, 0xa4, 0x92, 0x29, 0xde, 0xcb, 0x0e, 0x7b, 0x34, 0x15, - 0x66, 0x01, 0xae, 0x3a, 0x21, 0xa7, 0x9d, 0xfe, 0x5e, 0x24, 0xf5, 0x1d, 0x3c, 0xbb, 0x05, 0x1c, 0x5e, 0x10, 0x73, - 0xa8, 0x54, 0xf8, 0x71, 0xb6, 0x73, 0xce, 0x4e, 0x5a, 0xd1, 0x3c, 0xae, 0x7c, 0x7f, 0x28, 0xfd, 0xfa, 0x7b, 0x4a, - 0x10, 0xca, 0x84, 0x33, 0xf9, 0x18, 0x19, 0x89, 0x07, 0x88, 0x38, 0x22, 0xd0, 0x52, 0x3a, 0x16, 0x49, 0x69, 0x04, - 0xe4, 0x03, 0xac, 0x44, 0xbf, 0xca, 0x01, 0x29, 0x25, 0x41, 0x69, 0xf7, 0xff, 0xf6, 0xbf, 0xfe, 0xb7, 0xf4, 0x29, - 0x02, 0x5a, 0x01, 0x2c, 0xcc, 0xdc, 0xa8, 0x62, 0x67, 0xec, 0x02, 0xac, 0xd0, 0x78, 0xdc, 0x9a, 0x46, 0xc9, 0x04, - 0x20, 0x28, 0x98, 0xb8, 0xbb, 0x21, 0xeb, 0x81, 0x0a, 0x24, 0x58, 0x66, 0xd8, 0x59, 0x82, 0x57, 0x2f, 0xc2, 0x1d, - 0xfb, 0x43, 0x19, 0x7c, 0x2a, 0xb7, 0x94, 0x08, 0xda, 0xc8, 0xe7, 0x33, 0x68, 0xae, 0x96, 0xd3, 0xa7, 0x7e, 0x23, - 0x8c, 0x64, 0x1e, 0xac, 0x96, 0xd0, 0x07, 0x2d, 0x75, 0xa0, 0xe0, 0xdf, 0xfe, 0xf5, 0x3f, 0xff, 0x77, 0xf5, 0x8a, - 0xfe, 0xff, 0xbf, 0xfd, 0xcb, 0x3f, 0xfd, 0xdf, 0xff, 0xf3, 0x5f, 0x30, 0x39, 0x52, 0xc6, 0x08, 0xe8, 0x28, 0x59, - 0x55, 0x80, 0x40, 0x9c, 0xa9, 0x7a, 0xb6, 0xdf, 0x01, 0xcd, 0x42, 0x04, 0x29, 0x41, 0x22, 0x62, 0xa6, 0x24, 0x50, - 0x42, 0xd5, 0x0d, 0x38, 0x83, 0xfd, 0xb3, 0x28, 0x4a, 0x6d, 0x3f, 0x68, 0xdb, 0xd5, 0x9e, 0xf6, 0x8d, 0xbe, 0x3b, - 0xb8, 0x1b, 0x77, 0xca, 0x14, 0xf1, 0xf5, 0x5e, 0x2d, 0x95, 0xe3, 0x0a, 0x4b, 0xca, 0xaa, 0xdc, 0x42, 0x8f, 0xf2, - 0x12, 0x5f, 0x83, 0xae, 0x51, 0x4c, 0x5b, 0x5b, 0xeb, 0xd3, 0xfb, 0x65, 0x51, 0xf0, 0x78, 0x82, 0xfb, 0x21, 0xdc, - 0x63, 0x14, 0x0a, 0x6c, 0xa1, 0x4a, 0x92, 0x5c, 0x96, 0x34, 0x8a, 0x30, 0x61, 0xee, 0x3f, 0xfe, 0x87, 0xf2, 0x2f, - 0x33, 0x54, 0x05, 0x2c, 0x67, 0x16, 0x5d, 0x48, 0xc3, 0xe6, 0x61, 0xbb, 0x3d, 0xbf, 0x70, 0x97, 0xd5, 0x0c, 0xde, - 0x75, 0x93, 0x91, 0x4b, 0xcd, 0x1c, 0x90, 0x62, 0x88, 0xda, 0x7b, 0x07, 0xba, 0x7c, 0x1b, 0x9d, 0x3d, 0x65, 0xf9, - 0xf9, 0x92, 0x1c, 0x48, 0xf1, 0x6f, 0x18, 0xeb, 0x93, 0xbe, 0x36, 0x28, 0x31, 0x56, 0xb1, 0x34, 0x7a, 0x75, 0x45, - 0xaf, 0x69, 0x67, 0x35, 0xd3, 0xc4, 0x8c, 0x55, 0x9a, 0x51, 0x46, 0xcc, 0xc3, 0x80, 0x0e, 0xde, 0xb4, 0xbb, 0xd4, - 0xc3, 0x73, 0x9e, 0xcd, 0xcc, 0xe0, 0x24, 0x8b, 0xd8, 0x88, 0x4d, 0x94, 0x8f, 0x52, 0xd6, 0x8b, 0xc0, 0x63, 0xf9, - 0x19, 0x9e, 0x31, 0xc0, 0x6d, 0x16, 0xf1, 0x80, 0x28, 0xb5, 0x67, 0x86, 0x2f, 0x23, 0x0c, 0x0c, 0x67, 0x4b, 0x63, - 0xae, 0x9e, 0x68, 0x8a, 0x9e, 0xc0, 0x7a, 0x7e, 0x4a, 0xe9, 0x53, 0x77, 0x73, 0x28, 0xe1, 0x48, 0x78, 0x51, 0x65, - 0x87, 0x54, 0x26, 0xf6, 0xbb, 0x9a, 0x39, 0x2e, 0x99, 0x31, 0x18, 0xc1, 0xb7, 0x37, 0x16, 0x52, 0x52, 0x34, 0xfd, - 0x15, 0x94, 0x1f, 0x5a, 0x80, 0xdd, 0x6c, 0x45, 0x85, 0xd8, 0xea, 0x5d, 0xf8, 0x42, 0xab, 0xe2, 0xd1, 0x7c, 0x4e, - 0x0d, 0x5d, 0xa0, 0x53, 0x52, 0xa9, 0x91, 0x71, 0x50, 0x2c, 0x5c, 0x84, 0x9e, 0x65, 0x1b, 0x49, 0xd0, 0xe2, 0x49, - 0x06, 0xa5, 0xe9, 0xf7, 0x0d, 0xff, 0x3f, 0xdf, 0x8d, 0x21, 0x2b, 0x85, 0x78, 0x00, 0x00}; + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xbd, 0x7d, 0xc9, 0x76, 0xdb, 0xc8, 0xb2, 0xe0, 0xba, + 0xcf, 0xe9, 0x3f, 0xe8, 0x0d, 0x84, 0xd2, 0x93, 0x81, 0x12, 0x08, 0x91, 0x94, 0x65, 0xbb, 0x40, 0x41, 0xbc, 0xf2, + 0x50, 0xd7, 0xae, 0xf2, 0x54, 0x96, 0xec, 0x1a, 0x54, 0x2c, 0x0b, 0x22, 0x93, 0x22, 0xca, 0x20, 0xc0, 0x02, 0x92, + 0x1a, 0x8a, 0xc2, 0x3b, 0xbd, 0xea, 0x55, 0x9f, 0xd3, 0xe3, 0xe2, 0x6d, 0xfa, 0xbc, 0x5e, 0xf4, 0x47, 0xf4, 0xba, + 0x3f, 0xe5, 0xfe, 0x40, 0xbf, 0x4f, 0xe8, 0x88, 0xc8, 0x01, 0x09, 0x90, 0x1a, 0x5c, 0xaf, 0xfa, 0x1e, 0x0f, 0x02, + 0x72, 0x8c, 0x88, 0x8c, 0x8c, 0x29, 0x23, 0xa1, 0xdd, 0xb5, 0x51, 0x36, 0xe4, 0x97, 0x33, 0x66, 0x4d, 0xf8, 0x34, + 0xd9, 0xdb, 0x95, 0xff, 0xb3, 0x68, 0xb4, 0xb7, 0x9b, 0xc4, 0xe9, 0x27, 0x2b, 0x67, 0x49, 0x18, 0x0f, 0xb3, 0xd4, + 0x9a, 0xe4, 0x6c, 0x1c, 0x8e, 0x22, 0x1e, 0x05, 0xf1, 0x34, 0x3a, 0x65, 0xd6, 0xd6, 0xde, 0xee, 0x94, 0xf1, 0xc8, + 0x1a, 0x4e, 0xa2, 0xbc, 0x60, 0x3c, 0x7c, 0x7f, 0xf8, 0x75, 0xeb, 0xd1, 0xde, 0x6e, 0x31, 0xcc, 0xe3, 0x19, 0xb7, + 0x70, 0xc8, 0x70, 0x9a, 0x8d, 0xe6, 0x09, 0xdb, 0x3b, 0x8b, 0x72, 0xeb, 0x82, 0x85, 0x6f, 0x4e, 0x7e, 0x65, 0x43, + 0xee, 0x8f, 0xd8, 0x38, 0x4e, 0xd9, 0xdb, 0x3c, 0x9b, 0xb1, 0x9c, 0x5f, 0x7a, 0xcf, 0x56, 0x57, 0xc4, 0xac, 0xf0, + 0x0e, 0x74, 0xd5, 0x29, 0xe3, 0x6f, 0xce, 0x53, 0xd5, 0xe7, 0x29, 0x13, 0x93, 0x64, 0x79, 0xe1, 0xf1, 0x6b, 0xda, + 0x1c, 0x5c, 0x4e, 0x4f, 0xb2, 0xa4, 0xf0, 0x3e, 0xe9, 0xfa, 0x59, 0x9e, 0xf1, 0x0c, 0xc1, 0xf2, 0x27, 0x51, 0x61, + 0xb4, 0xf4, 0x9e, 0xac, 0x68, 0x32, 0x93, 0x95, 0x2f, 0x8a, 0x67, 0xe9, 0x7c, 0xca, 0xf2, 0xe8, 0x24, 0x61, 0x5e, + 0xc1, 0x42, 0x87, 0x79, 0xdc, 0x8b, 0xdd, 0x70, 0x8f, 0x5b, 0x71, 0x6a, 0xb1, 0xfe, 0x05, 0xa3, 0x92, 0x05, 0xd3, + 0xad, 0x82, 0xb5, 0xb6, 0x07, 0xe4, 0x1a, 0xc7, 0xa7, 0x73, 0xfd, 0x7e, 0x9e, 0xc7, 0x5c, 0x3d, 0x9f, 0x45, 0xc9, + 0x9c, 0x05, 0x71, 0xe9, 0x06, 0xec, 0x88, 0x0f, 0xc2, 0xd8, 0x7b, 0x42, 0x83, 0xc2, 0x90, 0x8b, 0x71, 0x96, 0x3b, + 0x48, 0xab, 0x18, 0xc7, 0xe6, 0x57, 0x57, 0x0e, 0x0f, 0x17, 0xa5, 0xeb, 0x7e, 0x62, 0xfe, 0x30, 0x4a, 0x12, 0x07, + 0x27, 0xde, 0xd8, 0x28, 0x70, 0xc6, 0xd8, 0xe3, 0x47, 0xf1, 0xc0, 0xed, 0xc5, 0x63, 0x87, 0x33, 0xb7, 0xea, 0x97, + 0x8d, 0x2d, 0xce, 0x1c, 0xee, 0xba, 0x4f, 0xae, 0xef, 0x93, 0x33, 0x3e, 0xcf, 0x01, 0xf6, 0xd2, 0x7b, 0xa3, 0x66, + 0x7e, 0x86, 0xf5, 0x07, 0xd4, 0xb1, 0x07, 0xb0, 0x17, 0xdc, 0xfa, 0x10, 0x9e, 0xc7, 0xe9, 0x28, 0x3b, 0xf7, 0x0f, + 0x26, 0x11, 0xfc, 0x78, 0x97, 0x65, 0x7c, 0x63, 0xc3, 0x39, 0xcb, 0xe2, 0x91, 0xd5, 0x0e, 0x43, 0xb3, 0xf2, 0xf2, + 0xc9, 0xc1, 0xc1, 0xd5, 0x55, 0xa3, 0xc0, 0x4f, 0x23, 0x1e, 0x9f, 0x31, 0xd1, 0x19, 0x00, 0xb0, 0xe1, 0xe7, 0x8c, + 0xb3, 0xd1, 0x01, 0xbf, 0x4c, 0xa0, 0x94, 0x31, 0x5e, 0xd8, 0x80, 0xe3, 0xd3, 0x6c, 0x08, 0x64, 0x4b, 0x0d, 0xc2, + 0x43, 0xd3, 0x9c, 0xcd, 0x92, 0x68, 0xc8, 0xb0, 0x1e, 0x46, 0xaa, 0x7a, 0x54, 0x8d, 0xbc, 0xaf, 0x43, 0xb1, 0xbc, + 0x8e, 0xeb, 0xc5, 0x2c, 0x4c, 0xd9, 0xb9, 0xf5, 0x2a, 0x9a, 0xf5, 0x86, 0x49, 0x54, 0x14, 0xc0, 0xaf, 0x0b, 0x42, + 0x21, 0x9f, 0x0f, 0x81, 0x41, 0x08, 0xc1, 0x05, 0x92, 0x69, 0x12, 0x17, 0xfe, 0xc7, 0xf5, 0x61, 0x51, 0xbc, 0x63, + 0xc5, 0x3c, 0xe1, 0xeb, 0x21, 0xac, 0x05, 0x5f, 0x0b, 0xc3, 0xaf, 0x5d, 0x3e, 0xc9, 0xb3, 0x73, 0xeb, 0x59, 0x9e, + 0x43, 0x73, 0x1b, 0xa6, 0x14, 0x0d, 0xac, 0xb8, 0xb0, 0xd2, 0x8c, 0x5b, 0x7a, 0x30, 0x5c, 0x40, 0xdf, 0x7a, 0x5f, + 0x30, 0xeb, 0x78, 0x9e, 0x16, 0xd1, 0x98, 0x41, 0xd3, 0x63, 0x2b, 0xcb, 0xad, 0x63, 0x18, 0xf4, 0x18, 0x96, 0xac, + 0xe0, 0xb0, 0x6b, 0x7c, 0xdb, 0xed, 0xd1, 0x5c, 0x50, 0x78, 0xc8, 0x2e, 0x78, 0xc8, 0x4a, 0x60, 0x4c, 0xab, 0xd0, + 0x68, 0x38, 0xee, 0x22, 0x81, 0x02, 0x16, 0xc6, 0x0c, 0x59, 0xd6, 0x31, 0x1b, 0xeb, 0xc5, 0xf9, 0xb0, 0xb1, 0xa1, + 0x69, 0x0d, 0x34, 0x71, 0xa0, 0x6d, 0xd1, 0x68, 0xeb, 0x09, 0xc4, 0x6b, 0x24, 0x72, 0x3d, 0xe6, 0x4b, 0xf2, 0x1d, + 0x5c, 0xa6, 0xc3, 0xfa, 0xd8, 0x50, 0x59, 0xf2, 0xec, 0x80, 0xe7, 0x71, 0x7a, 0x0a, 0x40, 0xc8, 0x99, 0xcc, 0x26, + 0x65, 0x29, 0x16, 0xff, 0x0d, 0x0b, 0x59, 0xb8, 0x87, 0xa3, 0xe7, 0xcc, 0xb1, 0x0b, 0xea, 0x61, 0x87, 0x21, 0x92, + 0x1e, 0x18, 0x8c, 0xf5, 0x59, 0xc0, 0x36, 0x6d, 0xdb, 0xfb, 0xda, 0xf5, 0x2e, 0x91, 0x83, 0x7c, 0xdf, 0x27, 0xf6, + 0x15, 0x9d, 0xe3, 0xb0, 0x83, 0x40, 0xfb, 0x09, 0x4b, 0x4f, 0xf9, 0xa4, 0xcf, 0x8e, 0xda, 0x83, 0x80, 0x03, 0x54, + 0xa3, 0xf9, 0x90, 0x39, 0xc8, 0x8f, 0x5e, 0x8e, 0xdb, 0x67, 0xd3, 0x81, 0x29, 0x70, 0x61, 0xd6, 0x08, 0xc7, 0xda, + 0xd2, 0xb8, 0x8a, 0x45, 0x15, 0x60, 0xc8, 0xe7, 0x36, 0xec, 0xb0, 0x13, 0x96, 0x1b, 0x70, 0xe8, 0x66, 0xbd, 0xda, + 0x0a, 0xce, 0x61, 0x85, 0xa0, 0x9f, 0x35, 0x9e, 0xa7, 0x43, 0x1e, 0x83, 0xe0, 0xb2, 0x37, 0x01, 0x5c, 0xb1, 0x72, + 0x7a, 0xe1, 0x6c, 0xb7, 0x74, 0x9d, 0xd8, 0xdd, 0x64, 0x47, 0xf9, 0x66, 0x67, 0xe0, 0x21, 0x94, 0x9a, 0xf8, 0x12, + 0xf1, 0x18, 0x10, 0x2c, 0xbd, 0xb7, 0x4c, 0x6f, 0xcf, 0x0f, 0x7d, 0xe6, 0x2f, 0xf3, 0x71, 0xc8, 0xfd, 0x69, 0x34, + 0x43, 0x6c, 0x18, 0xf1, 0x40, 0x94, 0x0e, 0x11, 0xba, 0xda, 0xba, 0x20, 0xc5, 0xfc, 0x8a, 0x05, 0x5c, 0x20, 0x08, + 0xec, 0xd9, 0x67, 0xd1, 0x70, 0x02, 0x5b, 0xbc, 0x22, 0xdc, 0x48, 0x6d, 0x87, 0x61, 0xce, 0x22, 0xce, 0x9e, 0x25, + 0x0c, 0xdf, 0x70, 0x05, 0xa0, 0xa7, 0xed, 0x7a, 0xb9, 0xda, 0x77, 0x49, 0xcc, 0x5f, 0x67, 0x30, 0x4f, 0x4f, 0x30, + 0x09, 0x70, 0x71, 0xbe, 0xb1, 0x11, 0x23, 0x8b, 0xec, 0x73, 0x58, 0xad, 0x93, 0x39, 0x08, 0x01, 0x3b, 0xc5, 0x16, + 0x36, 0x50, 0xdb, 0x8b, 0x7d, 0x0e, 0x44, 0x7c, 0x92, 0xa5, 0x1c, 0x86, 0x03, 0x78, 0x35, 0x07, 0xf9, 0xd1, 0x6c, + 0xc6, 0xd2, 0xd1, 0x93, 0x49, 0x9c, 0x8c, 0x80, 0x1a, 0x25, 0xe0, 0x9b, 0xb1, 0x10, 0xf0, 0x04, 0x64, 0x82, 0x9b, + 0x31, 0xa2, 0xe5, 0x43, 0x46, 0xe6, 0xa1, 0x6d, 0xf7, 0x50, 0x02, 0x49, 0x2c, 0x50, 0x06, 0xd1, 0xc2, 0xbd, 0x03, + 0xd1, 0x5f, 0xb8, 0x7c, 0x33, 0x8c, 0xf5, 0x32, 0x4a, 0x02, 0xbf, 0x41, 0x49, 0x03, 0xf4, 0x67, 0x20, 0x03, 0x7b, + 0x28, 0xb8, 0xbe, 0x97, 0x52, 0x27, 0x65, 0x0a, 0x43, 0x20, 0xc0, 0x10, 0x25, 0x88, 0xa4, 0xc1, 0xdb, 0x2c, 0xb9, + 0x1c, 0xc7, 0x49, 0x72, 0x30, 0x9f, 0xcd, 0xb2, 0x9c, 0x7b, 0xdf, 0x84, 0x0b, 0x9e, 0x55, 0xb8, 0xd2, 0x26, 0x2f, + 0xce, 0x63, 0x8e, 0x04, 0x75, 0x17, 0xc3, 0x08, 0x96, 0xfa, 0x71, 0x96, 0x25, 0x2c, 0x4a, 0x01, 0x0d, 0xd6, 0xb7, + 0xed, 0x20, 0x9d, 0x27, 0x49, 0xef, 0x04, 0x86, 0xfd, 0xd4, 0xa3, 0x6a, 0x21, 0xf1, 0x03, 0x7a, 0xde, 0xcf, 0xf3, + 0xe8, 0x12, 0x1a, 0x62, 0x1b, 0xe0, 0x45, 0x58, 0xad, 0x6f, 0x0e, 0xde, 0xbc, 0xf6, 0x05, 0xe3, 0xc7, 0xe3, 0x4b, + 0x00, 0xb4, 0xac, 0xa4, 0xe6, 0x38, 0xcf, 0xa6, 0x8d, 0xa9, 0x91, 0x0e, 0x71, 0xc8, 0x7a, 0xd7, 0x80, 0x10, 0xd3, + 0xc8, 0xb0, 0x4a, 0xcc, 0x84, 0xe0, 0x35, 0xf1, 0xb3, 0xac, 0xc4, 0x3d, 0xd0, 0xc7, 0x87, 0x40, 0x14, 0xc3, 0x94, + 0x37, 0x43, 0xcb, 0xf3, 0xcb, 0x45, 0x1c, 0x12, 0x9c, 0x33, 0xd4, 0xbf, 0x08, 0xe3, 0x30, 0x82, 0xd9, 0x17, 0x62, + 0xc0, 0x52, 0x41, 0x1c, 0x97, 0xa5, 0x97, 0x68, 0x26, 0x46, 0x89, 0x87, 0x02, 0x85, 0xc3, 0x36, 0xba, 0xba, 0x62, + 0xf0, 0xe2, 0x7a, 0xdf, 0x86, 0x8b, 0x48, 0xe1, 0x83, 0x1a, 0x0a, 0xf7, 0x57, 0x20, 0xe4, 0x04, 0x6a, 0xb2, 0x33, + 0xd0, 0x83, 0x00, 0xe7, 0x37, 0x1e, 0xe8, 0xff, 0x04, 0xa1, 0x58, 0xeb, 0x78, 0xa0, 0x41, 0x9f, 0x4c, 0xa2, 0xf4, + 0x94, 0x8d, 0x82, 0x84, 0x95, 0x52, 0xf2, 0xee, 0x5b, 0xb0, 0xc6, 0xc0, 0x4e, 0x85, 0xf5, 0xfc, 0xf0, 0xd5, 0x4b, + 0xb9, 0x72, 0x35, 0x61, 0x0c, 0x8b, 0x34, 0x07, 0xb5, 0x0a, 0x62, 0x5b, 0x8a, 0xe3, 0x67, 0x5c, 0x49, 0x6f, 0x51, + 0x12, 0x17, 0xef, 0x67, 0x60, 0x62, 0xb0, 0xb7, 0x30, 0x0c, 0x4c, 0x1f, 0xc2, 0x54, 0x54, 0x0e, 0xf3, 0x89, 0x8a, + 0x91, 0x2e, 0x82, 0xce, 0x02, 0x53, 0xf1, 0x9a, 0x39, 0x6e, 0x09, 0xac, 0xca, 0xe3, 0xa1, 0x15, 0x8d, 0x46, 0x2f, + 0xd2, 0x98, 0xc7, 0x51, 0x12, 0xff, 0x4e, 0x94, 0x5c, 0x20, 0x8f, 0xf1, 0x9e, 0x5c, 0x04, 0xc0, 0x9d, 0x7a, 0x24, + 0xae, 0x12, 0xb2, 0x6b, 0x44, 0x0c, 0x21, 0x2d, 0x93, 0xf0, 0x68, 0x20, 0xc1, 0x4b, 0xfc, 0xd9, 0xbc, 0x98, 0x20, + 0x61, 0xe5, 0xc0, 0x28, 0xc8, 0xb3, 0x93, 0x82, 0xe5, 0x67, 0x6c, 0xa4, 0x39, 0xa0, 0x00, 0xac, 0xa8, 0x39, 0x18, + 0x2f, 0x34, 0xa3, 0xa3, 0x74, 0x28, 0x83, 0xa1, 0x7a, 0xa6, 0x98, 0x65, 0x92, 0x99, 0xb5, 0x85, 0xa3, 0xa5, 0x80, + 0x23, 0x8c, 0x0a, 0x29, 0x09, 0xf2, 0x50, 0x61, 0x38, 0x01, 0x29, 0x04, 0x5a, 0xc1, 0xdc, 0xe6, 0x4a, 0x93, 0x3d, + 0x9b, 0x93, 0x4a, 0xc8, 0xa1, 0x23, 0x6c, 0x64, 0x82, 0x34, 0x77, 0x61, 0x57, 0x81, 0x94, 0x97, 0xe0, 0x0a, 0x29, + 0xa2, 0xcc, 0x1c, 0x64, 0x80, 0xf0, 0x5b, 0xa1, 0x0b, 0x7d, 0x6c, 0x41, 0x6c, 0xe0, 0xeb, 0x95, 0x07, 0xc2, 0x4a, + 0xbc, 0x2b, 0x44, 0xbc, 0x6b, 0xc0, 0xc6, 0x89, 0x91, 0x9f, 0xbc, 0x35, 0xee, 0xa7, 0xd9, 0xfe, 0x70, 0xc8, 0x8a, + 0x22, 0x03, 0xd8, 0xd6, 0xa8, 0xfd, 0x75, 0x86, 0x16, 0x50, 0xd2, 0xd5, 0xb2, 0xce, 0x2e, 0x48, 0x83, 0x9b, 0x6a, + 0x45, 0xe9, 0xf4, 0xc0, 0xfe, 0xf8, 0x11, 0x64, 0xb6, 0x27, 0xc9, 0x00, 0x54, 0x5f, 0x36, 0xfc, 0x84, 0x3d, 0x53, + 0xa7, 0xcc, 0x4a, 0xfb, 0xd2, 0xa9, 0x83, 0xe4, 0xc1, 0xb0, 0x6e, 0x69, 0x2c, 0xe8, 0xca, 0xa1, 0x71, 0x35, 0xa4, + 0x82, 0x5c, 0x9c, 0x92, 0xca, 0x36, 0x96, 0x11, 0xac, 0xb6, 0xd2, 0x23, 0xd2, 0x2b, 0x6c, 0x0a, 0x02, 0xf4, 0x88, + 0x0d, 0x7a, 0xb2, 0x3e, 0xcc, 0x05, 0xe5, 0x72, 0xf6, 0xdb, 0x9c, 0x15, 0x5c, 0xb0, 0x2e, 0x8c, 0x5b, 0xc0, 0xb8, + 0xe5, 0x92, 0x75, 0x58, 0xb3, 0x1d, 0x57, 0xc1, 0xf6, 0x66, 0x86, 0x7a, 0xac, 0x40, 0x4e, 0xbe, 0x99, 0x9d, 0x10, + 0x56, 0xe6, 0x5e, 0x5d, 0x7d, 0xab, 0x06, 0xa9, 0x96, 0x52, 0xdb, 0x40, 0x8d, 0x35, 0xb1, 0x55, 0x93, 0x91, 0xed, + 0x4a, 0x85, 0xba, 0xd6, 0xe9, 0xd5, 0xf8, 0x00, 0xf6, 0x5c, 0x5b, 0xb3, 0x74, 0x65, 0x6c, 0xbf, 0x55, 0x34, 0x7d, + 0x23, 0x46, 0x26, 0x6b, 0x94, 0xdd, 0xce, 0x3d, 0x6a, 0xc7, 0x43, 0xdb, 0xa5, 0xba, 0x4a, 0x30, 0xcc, 0xeb, 0x82, + 0xa1, 0x09, 0xf5, 0x4c, 0x77, 0xb1, 0x35, 0x53, 0xb1, 0x50, 0xad, 0xb5, 0x72, 0x20, 0x78, 0x78, 0x04, 0xc6, 0xc9, + 0x4a, 0xff, 0xe0, 0x75, 0x34, 0x65, 0x48, 0x51, 0xef, 0xba, 0x06, 0xd2, 0x81, 0x80, 0x26, 0x83, 0xa6, 0x7a, 0xe3, + 0xae, 0xb0, 0x9a, 0xea, 0xfb, 0x2b, 0x06, 0x2b, 0x02, 0xec, 0xeb, 0x72, 0xc5, 0x12, 0x91, 0xde, 0x14, 0x5c, 0xa2, + 0xe9, 0x23, 0xca, 0xc4, 0x9a, 0x90, 0x82, 0x07, 0xe4, 0x61, 0xf9, 0x1b, 0x0b, 0x27, 0x5b, 0x31, 0x85, 0x23, 0x47, + 0x99, 0x02, 0x74, 0x26, 0x25, 0x00, 0xe2, 0x92, 0x7e, 0xd6, 0x36, 0x16, 0x92, 0xed, 0x00, 0xf9, 0xc0, 0x1f, 0x27, + 0x11, 0x77, 0x3a, 0x5b, 0x6d, 0x17, 0xf8, 0x10, 0x84, 0x38, 0xe8, 0x08, 0x30, 0xef, 0x2b, 0x54, 0x18, 0xa2, 0x12, + 0xbb, 0xdc, 0x07, 0xa3, 0x68, 0x12, 0x8f, 0xb9, 0x93, 0xa1, 0x12, 0x71, 0x4b, 0x96, 0x80, 0x92, 0xd1, 0xfb, 0x0a, + 0xa4, 0x04, 0x17, 0xd2, 0x45, 0x54, 0x6b, 0x81, 0xa6, 0x20, 0x25, 0x29, 0x45, 0x5a, 0x50, 0x41, 0x60, 0x08, 0x95, + 0x9e, 0xe2, 0x28, 0xd0, 0x6f, 0x71, 0x5f, 0x0c, 0x1a, 0x2c, 0x59, 0x94, 0x71, 0x3f, 0x5e, 0x2e, 0x04, 0x35, 0xec, + 0xf3, 0xec, 0x65, 0x76, 0xce, 0xf2, 0x27, 0x11, 0xc2, 0x1e, 0x88, 0xee, 0x25, 0x48, 0x7a, 0x12, 0xe8, 0xac, 0xa7, + 0x78, 0xe5, 0x8c, 0x90, 0x86, 0x85, 0x98, 0xc6, 0xa8, 0x08, 0x41, 0xcb, 0x11, 0xed, 0x53, 0xdc, 0x52, 0xb4, 0xf7, + 0x50, 0x95, 0x30, 0xcd, 0x5b, 0xfb, 0x2f, 0xeb, 0xbc, 0x05, 0x23, 0xcc, 0x14, 0xb7, 0xd6, 0x77, 0xac, 0xeb, 0x49, + 0xdd, 0xec, 0x48, 0xde, 0x32, 0x94, 0x19, 0xe8, 0x8f, 0xab, 0xab, 0xca, 0x48, 0x07, 0x65, 0xaa, 0xa5, 0x39, 0x5a, + 0x4e, 0x62, 0x4b, 0xb8, 0x25, 0x28, 0x23, 0x34, 0xbc, 0xf2, 0x2c, 0x49, 0x0c, 0x5d, 0xe4, 0xc5, 0x3d, 0xa7, 0xa1, + 0x8e, 0x00, 0x8a, 0x69, 0x4d, 0x23, 0xf5, 0x79, 0xa0, 0x2b, 0x50, 0x29, 0x29, 0x6d, 0xe4, 0x55, 0x4d, 0x04, 0xc4, + 0xe9, 0x88, 0xe5, 0xc2, 0x41, 0x93, 0x3a, 0x14, 0x26, 0x4c, 0x81, 0xa1, 0xd9, 0x08, 0x24, 0xbc, 0x42, 0x00, 0xcc, + 0x13, 0x7f, 0x92, 0x15, 0x5c, 0xd7, 0x99, 0xd0, 0xc7, 0x57, 0x57, 0xb1, 0xf0, 0x17, 0x91, 0x01, 0x72, 0x36, 0xcd, + 0xce, 0xd8, 0x0a, 0xa8, 0x7b, 0x6a, 0x30, 0x13, 0x64, 0x63, 0x18, 0x50, 0xa2, 0xa0, 0x5a, 0x66, 0x49, 0x3c, 0x64, + 0x5a, 0x4b, 0x4d, 0x7d, 0x30, 0xe8, 0xd8, 0x05, 0xc8, 0x08, 0xe6, 0xee, 0xed, 0xed, 0xb5, 0xbd, 0x8e, 0x5b, 0x0a, + 0x82, 0x2f, 0x96, 0x28, 0x7a, 0x83, 0x7e, 0x94, 0x26, 0xf8, 0x2a, 0x59, 0xc0, 0x5d, 0x43, 0x29, 0x72, 0xe1, 0x27, + 0x79, 0x52, 0x10, 0xbb, 0xde, 0x08, 0x06, 0xe5, 0x4c, 0x09, 0x6e, 0x34, 0x71, 0xc5, 0xb6, 0x7d, 0xa7, 0xc9, 0xa6, + 0xd9, 0x49, 0xed, 0x30, 0xb5, 0x30, 0x72, 0xcd, 0x0b, 0xed, 0x01, 0x9b, 0xcb, 0x83, 0x56, 0x22, 0x55, 0x03, 0xaf, + 0x03, 0x84, 0xc2, 0xd3, 0x75, 0x96, 0x50, 0xaa, 0x3a, 0x4b, 0x21, 0xae, 0x37, 0xd0, 0x5b, 0x26, 0xc1, 0x5c, 0x45, + 0x82, 0x03, 0x29, 0x10, 0x38, 0x7a, 0x64, 0x62, 0xbd, 0x9e, 0xc0, 0xf2, 0x9c, 0x44, 0xc3, 0x4f, 0x1a, 0xdc, 0x8a, + 0xec, 0x4d, 0x36, 0x70, 0x1a, 0x25, 0xa1, 0x21, 0xae, 0x4c, 0xbc, 0x95, 0x84, 0xae, 0x6d, 0x14, 0x70, 0xc8, 0x96, + 0xd8, 0xbe, 0xb9, 0xd0, 0x4d, 0x6e, 0x97, 0xec, 0xa1, 0xfc, 0x27, 0xcd, 0x25, 0x37, 0xb0, 0x1c, 0x57, 0xd2, 0x80, + 0x2b, 0xc6, 0x83, 0xa5, 0x69, 0x40, 0x02, 0x7c, 0x57, 0x8e, 0xe2, 0xe2, 0x7a, 0x12, 0xfc, 0xa9, 0x60, 0x3e, 0x35, + 0x66, 0xba, 0x15, 0x52, 0x2d, 0xe1, 0xa4, 0x19, 0xac, 0x41, 0x93, 0xc6, 0x83, 0x12, 0x35, 0xdf, 0xa2, 0xa1, 0x42, + 0x1c, 0x7f, 0x2a, 0xaa, 0xd0, 0x04, 0x43, 0x30, 0x72, 0xaf, 0x90, 0x0c, 0x97, 0x2d, 0x8b, 0x16, 0x29, 0x53, 0x63, + 0x52, 0xa9, 0x9a, 0xe5, 0x32, 0x30, 0xb0, 0x68, 0xb7, 0xfa, 0xd2, 0x12, 0x57, 0x22, 0x37, 0x0d, 0xb5, 0x30, 0x29, + 0x94, 0x37, 0xe1, 0xe4, 0xe8, 0x77, 0x29, 0xeb, 0xdd, 0xc4, 0x27, 0x57, 0xf8, 0xe4, 0xbe, 0xe1, 0x43, 0x99, 0xbc, + 0x5d, 0xf4, 0x8b, 0xe0, 0x9b, 0x5a, 0x25, 0xda, 0xa7, 0x3e, 0x0a, 0x66, 0x57, 0x0b, 0x5d, 0x10, 0x28, 0x92, 0x4d, + 0xd2, 0xbe, 0xe4, 0x37, 0x14, 0x1b, 0x95, 0x67, 0x94, 0xb9, 0x62, 0x83, 0xd4, 0xbc, 0xd2, 0xcc, 0x4b, 0xdd, 0x86, + 0xfd, 0x5e, 0x96, 0x92, 0x4e, 0x5c, 0x50, 0x26, 0xf6, 0x6e, 0xa2, 0x8d, 0x97, 0x86, 0x99, 0xb0, 0x7e, 0x85, 0xb1, + 0x53, 0xa3, 0x50, 0x2a, 0x45, 0x20, 0x8e, 0x8d, 0xaf, 0x95, 0x65, 0x90, 0xf9, 0x2b, 0xec, 0x29, 0x00, 0x25, 0x81, + 0xc5, 0xd7, 0x54, 0xf2, 0xa2, 0xb0, 0x4e, 0xc7, 0x35, 0xa2, 0x63, 0x25, 0x42, 0x6b, 0x22, 0x5f, 0xeb, 0xb3, 0xd8, + 0xaf, 0xb9, 0x84, 0x26, 0x25, 0xf3, 0x7e, 0x1e, 0xd8, 0x2a, 0x10, 0x51, 0xe9, 0xb6, 0xa4, 0x9f, 0x90, 0x43, 0xba, + 0x4c, 0xf4, 0xda, 0x48, 0x06, 0xad, 0x53, 0x21, 0xd1, 0xd2, 0x41, 0x18, 0x39, 0xe8, 0xb8, 0xd3, 0x5a, 0x2c, 0x11, + 0xb2, 0x69, 0x6f, 0x12, 0x2b, 0xa2, 0x73, 0x9a, 0xa3, 0x09, 0x67, 0xea, 0x74, 0xc7, 0x01, 0x74, 0x40, 0xec, 0x2f, + 0xb1, 0xde, 0x4a, 0xb3, 0xd3, 0xf5, 0x2b, 0x87, 0xef, 0xea, 0x2a, 0x41, 0x7e, 0x10, 0x06, 0x2f, 0xac, 0x59, 0x5f, + 0xc9, 0xde, 0xfd, 0x97, 0xd8, 0x8a, 0xec, 0xcf, 0xaa, 0xa4, 0xf2, 0x14, 0x6a, 0x9c, 0x5b, 0x5f, 0x27, 0x66, 0x86, + 0x16, 0x55, 0xc5, 0x81, 0x21, 0xd5, 0x0f, 0x94, 0xc2, 0xae, 0x50, 0x3e, 0x90, 0x43, 0xc7, 0xae, 0xeb, 0x06, 0x39, + 0x39, 0x2f, 0x6b, 0xab, 0x5c, 0xc8, 0x8d, 0x0d, 0xd3, 0x67, 0x3a, 0xd3, 0xc3, 0x3f, 0x71, 0x50, 0x39, 0x17, 0x97, + 0x29, 0x59, 0x30, 0x4f, 0x94, 0x3a, 0x5a, 0x72, 0x40, 0xdb, 0x3d, 0xf4, 0xb4, 0xa3, 0xf3, 0x28, 0xe6, 0x96, 0x1e, + 0x45, 0x78, 0xda, 0x28, 0x9f, 0xa4, 0xd1, 0x01, 0x78, 0xa1, 0x09, 0x49, 0x4e, 0xb8, 0x69, 0x8b, 0x16, 0xc3, 0x09, + 0xc3, 0x10, 0xb8, 0xb2, 0x27, 0x4c, 0xd9, 0xb3, 0x86, 0x78, 0x8b, 0x81, 0xd9, 0x6a, 0xd8, 0xcb, 0x66, 0xf7, 0x9a, + 0xf9, 0x0f, 0x6b, 0x04, 0xb2, 0x6d, 0xaa, 0xea, 0xca, 0xc6, 0xbb, 0x14, 0x91, 0x18, 0x61, 0x5b, 0x35, 0xb6, 0xb4, + 0xf5, 0x7b, 0x0d, 0xf7, 0xba, 0x72, 0xcc, 0x6b, 0x4a, 0xb5, 0xa1, 0x87, 0x95, 0x9b, 0xc3, 0x4c, 0x47, 0x5e, 0xac, + 0xa0, 0xdb, 0x13, 0x41, 0x21, 0x70, 0x22, 0xb4, 0x3d, 0xa8, 0xb8, 0x81, 0x48, 0xc9, 0x95, 0x56, 0xcd, 0xe6, 0xc9, + 0x48, 0x02, 0x0b, 0x2e, 0x2c, 0x97, 0x7c, 0x74, 0x1e, 0x27, 0x49, 0x55, 0xfa, 0xa7, 0x0a, 0x78, 0x31, 0xec, 0x6d, + 0xa2, 0x5d, 0x60, 0x34, 0x57, 0x20, 0xb8, 0xda, 0x08, 0x7b, 0xef, 0xb8, 0xd5, 0xba, 0x8b, 0x88, 0x23, 0x37, 0xa3, + 0x11, 0x50, 0x8f, 0x11, 0x56, 0xcd, 0xda, 0xfb, 0xcf, 0x30, 0xa4, 0x66, 0xe0, 0x83, 0xea, 0x8c, 0x8a, 0x3f, 0xcb, + 0x9e, 0xfa, 0x4c, 0xf4, 0x6e, 0x55, 0x5d, 0xcd, 0x80, 0x8a, 0x0a, 0x7c, 0x98, 0x21, 0x96, 0xb6, 0x0a, 0x04, 0xe4, + 0x7a, 0x58, 0x94, 0x02, 0x26, 0x69, 0xb0, 0xa0, 0x14, 0x58, 0x6b, 0x65, 0xf7, 0xf2, 0xb6, 0x60, 0x0e, 0x85, 0xc2, + 0x45, 0xff, 0x27, 0xd9, 0x74, 0x86, 0x96, 0x59, 0x83, 0xa9, 0xa1, 0xc1, 0xfb, 0x46, 0x7d, 0xb9, 0xa2, 0xac, 0xd6, + 0x87, 0x76, 0x64, 0x8d, 0x9f, 0xb4, 0xa3, 0x0c, 0x0e, 0xd5, 0x5c, 0x17, 0xd5, 0xed, 0xe6, 0xa6, 0x88, 0x59, 0xc5, + 0xe3, 0x3e, 0xe9, 0x6d, 0x6d, 0x4d, 0x7a, 0x9a, 0x06, 0x24, 0x93, 0x24, 0xc3, 0x9b, 0x0c, 0x50, 0x56, 0xc4, 0x59, + 0x94, 0x0d, 0xf2, 0x2d, 0xca, 0x12, 0xd7, 0xef, 0xbb, 0xde, 0x7e, 0xcd, 0xb3, 0xf6, 0xf6, 0xaf, 0x77, 0x91, 0xab, + 0x3a, 0xe9, 0x41, 0x1e, 0x0d, 0xa0, 0x68, 0xc9, 0xa6, 0x0c, 0x17, 0xd3, 0x6c, 0xc4, 0x02, 0x1b, 0xba, 0xa7, 0x76, + 0xa9, 0xb4, 0x32, 0x6c, 0x8e, 0x94, 0x39, 0x8b, 0x77, 0xf5, 0x48, 0x6a, 0xb0, 0x0f, 0x2c, 0xa0, 0xcd, 0x85, 0xef, + 0xc3, 0xd3, 0x24, 0x3b, 0x89, 0x92, 0x43, 0xa1, 0xc0, 0x6b, 0x2d, 0x3f, 0x80, 0xcb, 0x48, 0x16, 0xab, 0xa1, 0xa4, + 0xbe, 0xef, 0x7f, 0x1f, 0xdc, 0xde, 0xa3, 0xf2, 0x56, 0xec, 0x8e, 0xdf, 0xf6, 0x3b, 0xb6, 0x8a, 0x88, 0xbd, 0x34, + 0xa7, 0x03, 0x8d, 0x53, 0x00, 0x65, 0x0e, 0x41, 0x93, 0x15, 0x5e, 0xc4, 0xc2, 0x97, 0xfd, 0x97, 0xca, 0xa5, 0xce, + 0xc0, 0x85, 0x00, 0x27, 0x3f, 0x89, 0x79, 0x0b, 0xcf, 0x23, 0x6d, 0x6f, 0x21, 0x2a, 0x30, 0xae, 0x48, 0x71, 0xe9, + 0x52, 0x79, 0x83, 0xde, 0xc7, 0xf0, 0x18, 0x9a, 0xad, 0xaf, 0x2f, 0x9c, 0x57, 0x11, 0x9f, 0xf8, 0x79, 0x94, 0x8e, + 0xb2, 0xa9, 0xe3, 0x6e, 0xda, 0xb6, 0xeb, 0x17, 0xe4, 0x89, 0x7c, 0xe5, 0x96, 0xeb, 0xc7, 0xde, 0x84, 0x85, 0x76, + 0xdf, 0xde, 0xfc, 0xe8, 0xbd, 0x67, 0xe1, 0xf1, 0xee, 0xfa, 0x62, 0xc2, 0xca, 0xbd, 0x63, 0xef, 0x42, 0xc7, 0xdc, + 0xbd, 0xb7, 0x28, 0x65, 0xa0, 0x57, 0xb8, 0x77, 0x21, 0xc1, 0x00, 0x76, 0xa3, 0xf8, 0x3b, 0x48, 0xb9, 0xf7, 0x74, + 0x20, 0x22, 0xe3, 0xb4, 0x57, 0x57, 0x76, 0x46, 0x11, 0x03, 0x7b, 0x4d, 0x3b, 0xab, 0x1b, 0x1b, 0x95, 0x9a, 0xaf, + 0x4a, 0xbd, 0x21, 0x0b, 0x6b, 0x9e, 0xba, 0x77, 0x48, 0x47, 0x2b, 0xf5, 0x8d, 0x3c, 0x64, 0xa4, 0x34, 0x97, 0xed, + 0x04, 0xc7, 0xd8, 0xe2, 0xab, 0xb7, 0xf5, 0x91, 0x88, 0x52, 0xf8, 0x31, 0x58, 0x2f, 0x11, 0xa8, 0x6f, 0x70, 0x70, + 0xbc, 0xc3, 0x70, 0x6b, 0xd7, 0xe9, 0x07, 0xce, 0x5a, 0xab, 0x75, 0xf5, 0xf3, 0xd6, 0xd1, 0x2f, 0x51, 0xeb, 0xf7, + 0xfd, 0xd6, 0x4f, 0x03, 0xf7, 0xca, 0xf9, 0x79, 0xab, 0x7f, 0x24, 0xdf, 0x8e, 0x7e, 0xd9, 0xfb, 0xb9, 0x18, 0x7c, + 0x29, 0x0a, 0xd7, 0x5d, 0x77, 0xeb, 0x14, 0x3c, 0xa5, 0x70, 0xab, 0xd5, 0xda, 0x83, 0xa7, 0x39, 0x3c, 0xe1, 0xcf, + 0x73, 0xf8, 0x71, 0x75, 0x64, 0xfd, 0x9b, 0x9f, 0xd3, 0x7f, 0xfb, 0x73, 0x3e, 0xc0, 0x31, 0x8f, 0x7e, 0xf9, 0xb9, + 0xb0, 0xef, 0xed, 0x85, 0x5b, 0x83, 0x4d, 0xd7, 0xd1, 0x35, 0x5f, 0x86, 0xd5, 0x23, 0xb4, 0x3a, 0xfa, 0x45, 0xbe, + 0xd9, 0xf7, 0x8e, 0x77, 0xf7, 0xc2, 0xc1, 0x95, 0x63, 0x5f, 0xdd, 0x73, 0xaf, 0x5c, 0xf7, 0x6a, 0x1d, 0xe7, 0x99, + 0xc1, 0xe8, 0xf7, 0xe0, 0xe7, 0x29, 0xfc, 0xb4, 0xe1, 0xe7, 0x19, 0xfc, 0xfc, 0x05, 0xba, 0x89, 0xf8, 0xdb, 0x15, + 0xc5, 0x42, 0xae, 0xf0, 0xc0, 0x22, 0x82, 0x55, 0x70, 0xd7, 0xb7, 0x62, 0x6f, 0x4c, 0x44, 0x83, 0x7d, 0xe8, 0xfb, + 0x3e, 0x86, 0x49, 0x9d, 0xc5, 0xc7, 0x75, 0x58, 0x74, 0xe4, 0x9c, 0xf5, 0x80, 0x79, 0x22, 0x72, 0x50, 0x04, 0x5c, + 0x9c, 0xad, 0x16, 0x78, 0xb8, 0xea, 0x8d, 0xc2, 0x31, 0x73, 0xc0, 0x28, 0x78, 0xce, 0xf0, 0xa1, 0xeb, 0x7a, 0xcf, + 0xe4, 0x99, 0x21, 0xee, 0x73, 0xc1, 0x5a, 0x69, 0x26, 0x4c, 0x1a, 0xdb, 0xf5, 0x66, 0x2b, 0x2a, 0x61, 0x5b, 0xa7, + 0xa7, 0x50, 0xb7, 0x2e, 0x0e, 0xda, 0xbe, 0x67, 0xd1, 0x27, 0xdc, 0x92, 0xaf, 0x8d, 0x43, 0xe0, 0x25, 0x4b, 0xbe, + 0x69, 0x34, 0x1a, 0x36, 0xa2, 0x70, 0xc7, 0x1e, 0x33, 0x98, 0x61, 0xc9, 0x44, 0xe4, 0xa4, 0x34, 0x85, 0x65, 0x0b, + 0x93, 0xbf, 0x8d, 0x72, 0xbe, 0x5e, 0x19, 0xb6, 0x61, 0xcd, 0x92, 0x6d, 0x5a, 0xfa, 0x77, 0x98, 0x02, 0x4d, 0x4b, + 0x3a, 0xff, 0x30, 0xc7, 0x0f, 0x53, 0x42, 0xeb, 0xb5, 0xc3, 0xc1, 0x43, 0x2f, 0x40, 0xee, 0x88, 0x7e, 0xce, 0x5b, + 0x54, 0x63, 0xf0, 0x57, 0x86, 0x19, 0x3c, 0x31, 0x1f, 0x86, 0x68, 0x16, 0xa5, 0x0e, 0x6e, 0xa5, 0x28, 0xee, 0x5f, + 0xe0, 0xce, 0x48, 0x4b, 0xef, 0x20, 0x54, 0x3b, 0xe6, 0x30, 0x67, 0xec, 0xfb, 0x28, 0xf9, 0xc4, 0x72, 0xe7, 0xc2, + 0xeb, 0x74, 0xbf, 0xa2, 0xce, 0x1e, 0xda, 0x66, 0xaf, 0xaa, 0x63, 0x34, 0x65, 0x16, 0xa8, 0x23, 0xc2, 0x56, 0xc7, + 0xcb, 0x31, 0xaa, 0x85, 0x24, 0x28, 0xbc, 0x2c, 0xec, 0x12, 0x87, 0xdb, 0xbb, 0xc5, 0xd9, 0xe9, 0x9e, 0x1d, 0xd8, + 0x36, 0x58, 0xfc, 0x87, 0x14, 0xb6, 0x12, 0x86, 0x05, 0x18, 0x64, 0xbb, 0x71, 0x8f, 0x6f, 0x6e, 0x56, 0x01, 0x27, + 0x3c, 0x48, 0xa7, 0xee, 0x89, 0x17, 0x79, 0x93, 0x10, 0x06, 0x1c, 0x42, 0x33, 0xec, 0xd2, 0x1b, 0xee, 0xc6, 0x72, + 0x1a, 0x8c, 0x85, 0xf8, 0x49, 0x54, 0xf0, 0x17, 0x18, 0x8f, 0x08, 0x87, 0x68, 0xec, 0xfb, 0xec, 0x82, 0x0d, 0x95, + 0x9d, 0x01, 0x84, 0x8a, 0xdc, 0x9e, 0x3b, 0x0c, 0x8d, 0x66, 0x30, 0x77, 0x18, 0x1e, 0xf6, 0x6d, 0xd8, 0x4b, 0xb0, + 0x2b, 0xc3, 0xe8, 0xa8, 0x33, 0xe8, 0xa7, 0x21, 0xc8, 0x5a, 0x4d, 0x5b, 0x59, 0x34, 0xaf, 0x15, 0x75, 0x07, 0x7d, + 0xe7, 0x0c, 0x8c, 0x74, 0xb0, 0xc5, 0x1d, 0x7c, 0xc3, 0x08, 0x45, 0x11, 0xbe, 0x63, 0xa7, 0xcf, 0x2e, 0x66, 0x8e, + 0xbd, 0xbb, 0x65, 0x6f, 0x62, 0xa9, 0x67, 0x03, 0x7b, 0xc1, 0xdc, 0xe1, 0xb9, 0x6b, 0x76, 0xde, 0x1e, 0x20, 0xa8, + 0x58, 0x88, 0x93, 0x9f, 0xf7, 0xed, 0x3d, 0x31, 0x75, 0x1b, 0x06, 0x4d, 0xe5, 0xf2, 0xe3, 0x8a, 0x1e, 0x12, 0xaa, + 0xaa, 0xab, 0x82, 0x0e, 0xca, 0xba, 0x81, 0x33, 0x31, 0x91, 0x68, 0xe1, 0x64, 0x92, 0x0a, 0xe0, 0xf0, 0x60, 0x33, + 0x98, 0xd4, 0xe8, 0xb6, 0x3d, 0xe8, 0x9f, 0x07, 0xf7, 0xec, 0x7b, 0xea, 0xe5, 0x94, 0x05, 0xe0, 0x5d, 0xd0, 0xf4, + 0xa7, 0xa8, 0x45, 0xe0, 0xe7, 0x8c, 0x01, 0x92, 0xe7, 0x54, 0x34, 0x92, 0x45, 0x73, 0x2c, 0x3a, 0x0c, 0x10, 0x54, + 0xaf, 0xd0, 0xd6, 0x9f, 0x58, 0x93, 0x51, 0x48, 0xb0, 0x6f, 0x6c, 0xc0, 0xd2, 0x6c, 0x76, 0x06, 0x78, 0xde, 0x90, + 0xf3, 0xe2, 0xfb, 0x98, 0x83, 0x4a, 0xd8, 0xda, 0xb3, 0xdd, 0xbe, 0x6d, 0xe1, 0xd2, 0xf6, 0xb2, 0xcd, 0x50, 0x50, + 0x38, 0xde, 0x7c, 0xcf, 0x82, 0xc9, 0x5e, 0xd8, 0xee, 0x3b, 0xb9, 0x50, 0x1d, 0x09, 0x9e, 0x5b, 0x0a, 0x09, 0xde, + 0xf6, 0x26, 0x20, 0xd0, 0x91, 0x73, 0xd7, 0xed, 0x4d, 0x55, 0x08, 0x45, 0x1f, 0x37, 0x47, 0x6e, 0x10, 0xc3, 0x0f, + 0xa7, 0x85, 0x4c, 0x33, 0xd1, 0x7d, 0xb5, 0x66, 0x76, 0x83, 0x91, 0xb2, 0xc8, 0x93, 0x30, 0xdb, 0x74, 0x30, 0x42, + 0x0b, 0x92, 0x76, 0xb7, 0x0f, 0x30, 0x6c, 0x3a, 0x8a, 0xd3, 0xb6, 0x14, 0xab, 0x29, 0xfb, 0xfc, 0xa8, 0x5a, 0x0e, + 0xd6, 0x8f, 0x98, 0x5f, 0x69, 0x1f, 0x00, 0x2b, 0x48, 0xbc, 0x7c, 0xa0, 0xce, 0xbc, 0x9e, 0xd7, 0xce, 0xb7, 0x16, + 0x4a, 0x14, 0x31, 0xcf, 0x90, 0x50, 0xbc, 0xd4, 0x6e, 0x98, 0x30, 0xb7, 0x67, 0x48, 0x0c, 0xcd, 0xf2, 0x61, 0x1b, + 0x98, 0x5e, 0x05, 0xd8, 0x53, 0x73, 0x5b, 0x24, 0x61, 0xd5, 0xdc, 0x3b, 0x02, 0xd6, 0x1e, 0x84, 0xaf, 0xc4, 0x89, + 0x63, 0x4f, 0x45, 0xf3, 0x59, 0x12, 0x3e, 0x6f, 0x1c, 0x17, 0x47, 0x78, 0x22, 0x74, 0xe0, 0x0f, 0xe7, 0x39, 0xc8, + 0x03, 0xfe, 0x1a, 0x2c, 0x83, 0x50, 0x36, 0x45, 0x47, 0x0f, 0x8f, 0x80, 0x3d, 0x42, 0xbc, 0x11, 0x36, 0x37, 0xaa, + 0xd1, 0xa2, 0x24, 0xe3, 0x85, 0x0e, 0x86, 0x7b, 0x5c, 0xba, 0xf6, 0x28, 0x18, 0xe4, 0x89, 0xb1, 0x83, 0x67, 0xfe, + 0xfe, 0x10, 0xab, 0x71, 0x82, 0xc2, 0x2d, 0x69, 0xb7, 0x55, 0xe2, 0xef, 0xc0, 0x4f, 0x41, 0x82, 0x63, 0x1d, 0xf8, + 0x59, 0x1b, 0x1b, 0x89, 0x44, 0x6a, 0x37, 0xed, 0xd1, 0x49, 0x04, 0xc6, 0x83, 0x73, 0x3f, 0x85, 0x6a, 0x24, 0x11, + 0x15, 0xe5, 0x68, 0x81, 0x9a, 0xa7, 0x6a, 0x15, 0x7c, 0x47, 0x66, 0x04, 0x9e, 0x63, 0xd8, 0x9a, 0xfc, 0x54, 0xdd, + 0x58, 0xc4, 0xf2, 0x5d, 0x97, 0x8e, 0xb6, 0xf0, 0x00, 0x52, 0x30, 0x9a, 0x60, 0x18, 0x97, 0x82, 0x92, 0x15, 0xff, + 0x7d, 0x34, 0x62, 0xe5, 0x93, 0xa3, 0x6c, 0x73, 0x73, 0x20, 0xce, 0x2d, 0x88, 0x71, 0xb8, 0x11, 0x5d, 0x8d, 0x2b, + 0x00, 0xea, 0xd3, 0x39, 0x71, 0x3d, 0x30, 0xad, 0x58, 0xd3, 0xa5, 0xd8, 0x27, 0x87, 0x19, 0x80, 0x82, 0x5b, 0xce, + 0x91, 0xdf, 0xff, 0xcb, 0x00, 0xdc, 0x63, 0xff, 0x4b, 0x77, 0x4b, 0x09, 0x9a, 0x9e, 0x3c, 0x53, 0x5c, 0xd0, 0x19, + 0x6b, 0xc7, 0xa3, 0xd8, 0x68, 0x50, 0x78, 0x29, 0x60, 0x00, 0xda, 0x1c, 0x64, 0x42, 0xc5, 0x41, 0xc8, 0x51, 0x81, + 0xed, 0xe3, 0xe6, 0xe7, 0xb8, 0xb3, 0x9f, 0x82, 0x85, 0xd7, 0xd7, 0x6f, 0x8f, 0xe1, 0xed, 0x2f, 0xfa, 0xed, 0x25, + 0x0b, 0x7e, 0x2d, 0x65, 0xe8, 0xbe, 0x36, 0xc5, 0x03, 0x35, 0x45, 0x29, 0x96, 0xc8, 0xa0, 0x21, 0x73, 0xf3, 0xa5, + 0x98, 0x0d, 0x77, 0x4b, 0x54, 0x3b, 0x52, 0x74, 0xe5, 0x3e, 0x8f, 0x4e, 0x91, 0xb8, 0xae, 0x49, 0x0a, 0x23, 0x97, + 0xc0, 0x44, 0xb8, 0xe2, 0x5b, 0x62, 0xce, 0xbd, 0x36, 0xd8, 0xe0, 0xb5, 0xbc, 0x03, 0xb4, 0xef, 0xd8, 0x74, 0xc6, + 0x2f, 0x0f, 0x48, 0xd1, 0x07, 0x32, 0x6d, 0x40, 0x9c, 0x9d, 0xb7, 0x7b, 0xf1, 0x2e, 0xef, 0xc5, 0x20, 0xd5, 0x73, + 0xc5, 0x62, 0xb8, 0x57, 0xbd, 0xb7, 0x18, 0xa5, 0x34, 0x99, 0xc9, 0xab, 0xa1, 0xd7, 0x95, 0xe8, 0x6d, 0x6e, 0x02, + 0x82, 0x3d, 0xa3, 0x2b, 0x17, 0x5d, 0xcb, 0x52, 0xd0, 0x04, 0x20, 0x7a, 0x54, 0x67, 0x39, 0xe2, 0x38, 0xcc, 0x66, + 0x43, 0xc1, 0xc1, 0xdc, 0x95, 0xa3, 0xe2, 0x98, 0xd8, 0x5d, 0x26, 0xec, 0x00, 0x66, 0xc4, 0xe5, 0xad, 0x8e, 0x88, + 0x0e, 0x8b, 0xfe, 0x3a, 0xbe, 0xfd, 0xd1, 0x63, 0x9b, 0x1d, 0x17, 0x34, 0x48, 0x6d, 0xac, 0x87, 0xd5, 0x58, 0x50, + 0x1f, 0x7e, 0xd4, 0x54, 0x2a, 0x8b, 0xcd, 0xcd, 0xb2, 0x7e, 0x54, 0xab, 0x76, 0x70, 0xed, 0x34, 0xe5, 0xa2, 0x99, + 0x0d, 0xc2, 0x81, 0x88, 0x09, 0x14, 0x68, 0x69, 0x65, 0xc5, 0x00, 0x43, 0xca, 0x72, 0x94, 0x4f, 0x21, 0xf3, 0xe2, + 0xb2, 0xd4, 0xa9, 0x2f, 0x32, 0x1e, 0x19, 0xe2, 0xa9, 0x27, 0x19, 0x2b, 0xa0, 0x60, 0xbd, 0xd4, 0x4b, 0x68, 0x89, + 0x00, 0xf3, 0x67, 0x2a, 0x87, 0x46, 0x58, 0x20, 0x51, 0x68, 0x98, 0x25, 0xca, 0xf8, 0x2c, 0xc2, 0x18, 0xb4, 0xfd, + 0x93, 0x5a, 0xec, 0xab, 0x50, 0x46, 0x47, 0x71, 0x94, 0x0f, 0x02, 0xaa, 0x9f, 0x4b, 0x09, 0x36, 0x09, 0xdf, 0x03, + 0x1b, 0x55, 0x8e, 0x27, 0x09, 0xc2, 0xa7, 0x71, 0xce, 0xc8, 0x53, 0x58, 0x97, 0x30, 0x4b, 0xd3, 0x36, 0x52, 0xed, + 0x22, 0x33, 0x08, 0xe5, 0xc2, 0xfc, 0x13, 0xe3, 0xec, 0x22, 0x0b, 0x97, 0x5a, 0x83, 0xf9, 0xf1, 0xc6, 0x04, 0x28, + 0xbb, 0xba, 0xca, 0x84, 0x8f, 0x1b, 0x91, 0xbd, 0xa1, 0x2b, 0x26, 0x7d, 0x85, 0x54, 0xe0, 0x44, 0x64, 0xf1, 0xd0, + 0x19, 0x0a, 0x8d, 0x70, 0x48, 0xa7, 0xc8, 0xb9, 0x6b, 0x6c, 0xfa, 0xbc, 0xaf, 0x7d, 0xa3, 0x34, 0x74, 0x12, 0x10, + 0x02, 0x02, 0x77, 0xc3, 0x9a, 0x4a, 0xfb, 0x69, 0x90, 0x50, 0x29, 0xfa, 0x39, 0x80, 0x7f, 0x18, 0x49, 0x0a, 0x80, + 0xfd, 0x50, 0x8d, 0x14, 0x51, 0x96, 0x05, 0x2e, 0x00, 0xcd, 0x75, 0x80, 0x2b, 0xe1, 0x0b, 0x03, 0x15, 0xa6, 0xa7, + 0x59, 0x79, 0x29, 0x94, 0xc8, 0xbb, 0x15, 0x29, 0x6b, 0x24, 0x93, 0xcf, 0xd0, 0xe1, 0x53, 0xde, 0xf5, 0x6b, 0x89, + 0x87, 0x2e, 0x78, 0x0a, 0xcb, 0xaa, 0x9e, 0x5f, 0x85, 0x9c, 0x9c, 0x6b, 0xd0, 0x15, 0x52, 0xe8, 0x2f, 0x39, 0xc9, + 0xfb, 0xaf, 0xfc, 0xaa, 0x96, 0x1a, 0x43, 0xd9, 0xfb, 0x55, 0xcd, 0xb0, 0xbc, 0x9c, 0x55, 0x61, 0x0a, 0x02, 0x6e, + 0xc1, 0x92, 0x60, 0x21, 0x35, 0x04, 0x58, 0xd8, 0x1e, 0x69, 0xa5, 0x20, 0x2f, 0x75, 0x78, 0xe7, 0x29, 0x58, 0x01, + 0xc6, 0xa1, 0x96, 0x4a, 0xa6, 0x91, 0xc4, 0x97, 0x4a, 0x14, 0x98, 0xf2, 0x60, 0x08, 0x7e, 0x6a, 0xf3, 0xa4, 0xeb, + 0xc2, 0xf5, 0xe3, 0x29, 0xa6, 0xf6, 0x10, 0xe8, 0xb1, 0xb7, 0x06, 0xa6, 0x44, 0x5d, 0x87, 0x15, 0xc4, 0xa1, 0x59, + 0x4d, 0xb3, 0x80, 0x19, 0xd3, 0x06, 0x2d, 0xd9, 0x06, 0x5b, 0x2e, 0x07, 0xfb, 0x48, 0x6c, 0xcf, 0x6a, 0x05, 0x84, + 0xae, 0x41, 0x03, 0x43, 0xee, 0x52, 0xa1, 0x85, 0x79, 0xaf, 0x4b, 0x45, 0xb8, 0x3f, 0xfb, 0x5c, 0x5a, 0xc1, 0x99, + 0x97, 0xd1, 0xc0, 0x07, 0xf1, 0x49, 0x82, 0x89, 0x2f, 0x88, 0x15, 0xd8, 0xc1, 0x41, 0xa7, 0xd9, 0x14, 0x38, 0x15, + 0x17, 0x29, 0x83, 0x65, 0x45, 0xa9, 0x0d, 0x3f, 0xa4, 0xc8, 0xd6, 0x5d, 0x1e, 0xe8, 0x2e, 0xc4, 0x02, 0xd8, 0xe9, + 0x57, 0x8c, 0x7c, 0xcb, 0x7a, 0x19, 0x30, 0x38, 0xd3, 0x1a, 0x07, 0x81, 0xdf, 0xdc, 0x4c, 0x06, 0x65, 0x4a, 0x6c, + 0xd7, 0x64, 0x75, 0x01, 0x39, 0x0c, 0xd5, 0xc4, 0x1d, 0x84, 0xa5, 0xb2, 0xc7, 0x8b, 0x72, 0x8a, 0xcb, 0xa5, 0x2c, + 0xe4, 0xe6, 0x79, 0x35, 0xcd, 0xe7, 0x56, 0x9a, 0x4d, 0xc7, 0x5b, 0xf1, 0x45, 0xc1, 0xdf, 0x77, 0x62, 0x69, 0xd5, + 0x53, 0x6a, 0x85, 0x47, 0x99, 0x5b, 0xb2, 0x4e, 0x49, 0xad, 0xae, 0x1b, 0xa8, 0x46, 0x78, 0x9a, 0x86, 0x8d, 0x40, + 0x88, 0x09, 0x2e, 0x7e, 0xdd, 0x64, 0x62, 0xda, 0x5b, 0x42, 0xea, 0x08, 0xbb, 0x87, 0x72, 0x82, 0xbb, 0x9a, 0x67, + 0x9f, 0x87, 0xb3, 0xeb, 0x99, 0x7b, 0xdf, 0x60, 0xee, 0xc7, 0x21, 0x37, 0x18, 0x3d, 0x96, 0x09, 0x3f, 0x32, 0xf6, + 0x91, 0xab, 0xaa, 0x27, 0xa7, 0x61, 0x25, 0xb2, 0xc4, 0x93, 0x71, 0xd4, 0x61, 0x9c, 0x8a, 0xd6, 0x04, 0xd9, 0xd5, + 0x55, 0x61, 0xee, 0x05, 0x0a, 0x9a, 0x7a, 0xbc, 0x1e, 0xa7, 0xad, 0xd8, 0xd9, 0x88, 0x44, 0xee, 0xbf, 0xaa, 0x45, + 0x22, 0x2b, 0x3e, 0xc7, 0x91, 0xae, 0x39, 0xc8, 0x7d, 0x72, 0xba, 0xbc, 0x49, 0x85, 0x6e, 0xd1, 0x68, 0x1b, 0x7b, + 0x54, 0x1f, 0x48, 0xea, 0x19, 0x15, 0x58, 0xd5, 0xd8, 0x1b, 0x1b, 0x1d, 0x91, 0x6e, 0xa9, 0x14, 0x1b, 0x2c, 0x2d, + 0x8c, 0x66, 0x8c, 0x82, 0x41, 0x49, 0x91, 0x81, 0x1a, 0xe5, 0xd7, 0x08, 0x86, 0x7d, 0x6a, 0x00, 0x8a, 0x73, 0x75, + 0xf5, 0xe3, 0x52, 0xb2, 0x85, 0x80, 0xc4, 0x5d, 0x30, 0x10, 0x6b, 0x82, 0x99, 0x91, 0x4f, 0xde, 0x03, 0xe7, 0xf5, + 0x19, 0xfa, 0x08, 0xe0, 0x17, 0x88, 0x4d, 0x0f, 0x26, 0xb6, 0x4d, 0x44, 0xd1, 0x67, 0x03, 0xcf, 0x01, 0xd8, 0x59, + 0x15, 0x1a, 0x7d, 0x57, 0xa5, 0x80, 0x21, 0x1b, 0xb8, 0x01, 0xab, 0xc2, 0x72, 0xfb, 0xcf, 0xc1, 0x6d, 0x80, 0xd7, + 0x67, 0xb2, 0xf9, 0x3a, 0xe6, 0x09, 0x56, 0x67, 0x17, 0x7e, 0x65, 0x59, 0x8b, 0x73, 0xa7, 0xc3, 0x46, 0xbd, 0xa2, + 0x84, 0xa8, 0x3d, 0xc0, 0xda, 0x43, 0x8c, 0xb0, 0x88, 0xf7, 0x57, 0xf8, 0xae, 0xc7, 0x2d, 0xf7, 0x35, 0x5a, 0x84, + 0xe9, 0x32, 0x69, 0x0c, 0x4a, 0xd6, 0xfd, 0x64, 0xc4, 0xbd, 0x3c, 0x10, 0xb1, 0xe0, 0x0a, 0x47, 0x56, 0x85, 0x14, + 0x1b, 0x48, 0xd2, 0xd3, 0x3e, 0x1d, 0xb0, 0xaf, 0x37, 0x7b, 0x01, 0x65, 0xde, 0x57, 0xa4, 0x92, 0x90, 0xd2, 0xec, + 0x86, 0x48, 0x12, 0xd6, 0x8a, 0x3c, 0x75, 0x3e, 0x70, 0xb4, 0xcf, 0xad, 0x24, 0x82, 0x11, 0x9c, 0x84, 0xe9, 0x58, + 0x79, 0xd8, 0x14, 0xe0, 0x2a, 0x3a, 0x62, 0xfa, 0x26, 0x20, 0xbf, 0x19, 0xc8, 0xed, 0xa5, 0xe4, 0xda, 0x5c, 0xc3, + 0xf0, 0x04, 0x09, 0x56, 0x45, 0x22, 0xf0, 0x88, 0x1a, 0x70, 0xcc, 0x57, 0x79, 0x1e, 0x60, 0xc2, 0xd7, 0xf6, 0x26, + 0x00, 0x94, 0x93, 0xab, 0xe2, 0x2c, 0x05, 0xba, 0x01, 0xcb, 0xd5, 0x71, 0x6a, 0x54, 0x24, 0x2e, 0x6e, 0x4c, 0x57, + 0xb7, 0xf4, 0xa7, 0x68, 0x39, 0x93, 0x21, 0xa6, 0x83, 0x20, 0x20, 0x53, 0xdf, 0x31, 0x47, 0xc8, 0x5c, 0x61, 0x7d, + 0xce, 0x9c, 0xda, 0xd4, 0x3d, 0x46, 0xdd, 0x3c, 0x49, 0x2d, 0x5e, 0xa7, 0x4d, 0x29, 0x11, 0x93, 0x12, 0x73, 0x5d, + 0xa4, 0x62, 0x33, 0x25, 0xee, 0xdc, 0xfa, 0x46, 0x0b, 0x69, 0xa3, 0xad, 0x8b, 0x1c, 0x6c, 0x56, 0xc9, 0x7b, 0x02, + 0xe3, 0xb9, 0x20, 0x7c, 0xf9, 0x8a, 0x92, 0x74, 0x98, 0x63, 0x22, 0x58, 0xbd, 0x98, 0x8a, 0xfc, 0x9d, 0xa3, 0xd3, + 0xec, 0x0d, 0x7a, 0x90, 0x7a, 0x03, 0x89, 0x59, 0x13, 0xdf, 0x85, 0x34, 0xd4, 0x11, 0x02, 0x95, 0x51, 0x2d, 0xd3, + 0x71, 0x62, 0x15, 0xbe, 0x11, 0x7c, 0xf5, 0x56, 0x1f, 0xe7, 0x1b, 0xcf, 0x8d, 0xd5, 0x08, 0x62, 0xf0, 0x16, 0xf2, + 0x81, 0x27, 0x45, 0x38, 0x10, 0x2e, 0xdf, 0xdc, 0xec, 0xe5, 0xbb, 0xbc, 0x0a, 0x91, 0x54, 0x30, 0xc6, 0x98, 0x51, + 0x8c, 0x7b, 0xa2, 0xa6, 0x16, 0x73, 0x18, 0x58, 0xb6, 0x0e, 0x73, 0x3c, 0x00, 0x80, 0x96, 0xa6, 0xf4, 0xaa, 0xa9, + 0x50, 0x79, 0x9e, 0x4b, 0xf8, 0x54, 0x87, 0xa8, 0xaa, 0xf1, 0xdb, 0xd5, 0x19, 0x28, 0x04, 0xf7, 0x5a, 0xc7, 0xc3, + 0x43, 0x08, 0x58, 0x45, 0x21, 0x0b, 0xf4, 0x06, 0xed, 0x55, 0x89, 0x50, 0xcc, 0x9c, 0xac, 0xc7, 0x0c, 0x27, 0x15, + 0x6c, 0xa1, 0x12, 0x96, 0x4a, 0x0b, 0xfc, 0x6a, 0x23, 0x34, 0x4f, 0x19, 0xf7, 0x5f, 0x55, 0x38, 0x83, 0xfe, 0x60, + 0xde, 0x32, 0xa3, 0xbe, 0x5d, 0x3a, 0x91, 0xa9, 0xc0, 0xc4, 0xcd, 0x2c, 0xb5, 0xdf, 0xaf, 0xab, 0xb4, 0x9f, 0x57, + 0xc8, 0x7d, 0x4e, 0x9a, 0xaf, 0x73, 0x07, 0xcd, 0x27, 0xc3, 0xfd, 0x4a, 0xf9, 0xa1, 0x85, 0x51, 0x53, 0x7e, 0x79, + 0x5d, 0xf9, 0x15, 0x9e, 0x0a, 0x6f, 0xed, 0x75, 0x51, 0xe8, 0xa2, 0x3e, 0x07, 0x43, 0x48, 0x3f, 0x82, 0x6b, 0x68, + 0xf0, 0xa0, 0x48, 0x16, 0x8b, 0xb5, 0x0b, 0xe2, 0xfa, 0x98, 0x53, 0xed, 0x50, 0xc6, 0x18, 0xf1, 0xb4, 0xe4, 0x20, + 0xc9, 0xe0, 0x60, 0xfc, 0x06, 0x06, 0xc4, 0xa4, 0x24, 0xa4, 0x43, 0xe8, 0xac, 0xcc, 0x44, 0x54, 0xee, 0xe2, 0xed, + 0xc6, 0x65, 0x4d, 0xa1, 0x08, 0x3b, 0xc1, 0x4c, 0xa5, 0x54, 0x10, 0x48, 0x93, 0x6f, 0xad, 0x53, 0x0b, 0x86, 0x16, + 0xae, 0xa9, 0x80, 0xbc, 0xb6, 0xeb, 0x41, 0x93, 0xf7, 0x14, 0x43, 0x5f, 0xa5, 0x46, 0xbc, 0xcc, 0xe0, 0x6b, 0xd8, + 0xfc, 0x35, 0x51, 0x92, 0x87, 0x4c, 0xc4, 0x5e, 0xc1, 0x27, 0x42, 0x36, 0x05, 0x3b, 0x13, 0xe8, 0x87, 0x76, 0x65, + 0x2f, 0xdd, 0x2d, 0x2a, 0x97, 0x16, 0x8d, 0xad, 0x44, 0xcd, 0x9a, 0x1f, 0xc5, 0x9b, 0x29, 0xec, 0x67, 0x8f, 0x12, + 0x08, 0x48, 0x53, 0x39, 0x49, 0x35, 0xef, 0x51, 0x3a, 0x00, 0x90, 0x60, 0xf7, 0x13, 0x58, 0xe8, 0x37, 0x25, 0x26, + 0x58, 0x54, 0x8d, 0xdd, 0x66, 0xa0, 0x35, 0x67, 0xa4, 0xf9, 0x66, 0xa8, 0xb5, 0x37, 0x95, 0xf5, 0x8c, 0xd9, 0x01, + 0xb6, 0xed, 0x6e, 0x16, 0x47, 0xe9, 0x66, 0x67, 0x60, 0x08, 0x2e, 0x3c, 0xfe, 0x4f, 0x4a, 0x4c, 0x03, 0xc9, 0xa5, + 0x6e, 0xfc, 0x84, 0x3a, 0x0c, 0xff, 0x5b, 0x90, 0x02, 0xee, 0xd7, 0x56, 0x63, 0xc9, 0xb9, 0x57, 0x1c, 0x25, 0x97, + 0x55, 0xb5, 0xab, 0x25, 0x68, 0xe8, 0x46, 0x32, 0x26, 0x8a, 0x79, 0x4e, 0x00, 0x8c, 0x62, 0xf3, 0xa7, 0x4c, 0x27, + 0x79, 0xff, 0xba, 0x32, 0xb5, 0xdb, 0xf7, 0xfd, 0x28, 0x3f, 0xa5, 0x23, 0x15, 0x95, 0xcd, 0x49, 0xcc, 0xbf, 0x2d, + 0xc0, 0x34, 0x27, 0x3e, 0xd2, 0x73, 0x0d, 0x42, 0x01, 0xbe, 0xb2, 0xa1, 0xd4, 0x6c, 0x8f, 0xff, 0xe8, 0x6c, 0xf7, + 0x25, 0x51, 0x04, 0x0b, 0x34, 0xe8, 0x72, 0x0d, 0xbe, 0x80, 0x65, 0x70, 0x47, 0xfa, 0x29, 0xf8, 0x5e, 0x5e, 0x07, + 0x9f, 0xb1, 0xff, 0x05, 0xa0, 0x55, 0x81, 0x01, 0xe5, 0x4e, 0xd3, 0xb0, 0x12, 0xe2, 0x12, 0x15, 0x66, 0x15, 0xe7, + 0x8f, 0xeb, 0xbc, 0x6e, 0x5a, 0x96, 0x18, 0x94, 0x9f, 0xb9, 0x86, 0x1b, 0xdf, 0x6b, 0xe4, 0x8f, 0xef, 0x3f, 0x07, + 0xdd, 0x4e, 0xa4, 0xdd, 0xd8, 0xc8, 0xd7, 0xc8, 0x42, 0xc3, 0x7b, 0x61, 0x33, 0x68, 0x8b, 0x74, 0xc9, 0xd5, 0x33, + 0x16, 0xe3, 0x6d, 0x11, 0x2a, 0xc3, 0x07, 0x2c, 0x98, 0x01, 0x86, 0xe0, 0xb1, 0x53, 0x99, 0x7c, 0x86, 0x8d, 0xa6, + 0xd8, 0x35, 0x17, 0x06, 0x1f, 0xa8, 0xca, 0x42, 0xf2, 0x62, 0x9d, 0x6c, 0xcf, 0xce, 0xe0, 0xf9, 0x65, 0x5c, 0x00, + 0x75, 0x00, 0xfd, 0x8a, 0xca, 0x62, 0x03, 0xb9, 0xb8, 0x29, 0x6b, 0xbd, 0xa2, 0xd1, 0xe8, 0xc6, 0x2e, 0xac, 0xae, + 0xc0, 0x27, 0x51, 0x3a, 0x4a, 0xc4, 0x24, 0x66, 0x52, 0xe5, 0x8a, 0x5c, 0x1b, 0xdd, 0x4b, 0x5b, 0x34, 0xcf, 0x85, + 0x04, 0xaf, 0x08, 0xdc, 0x10, 0xfa, 0x4a, 0x5f, 0xae, 0x36, 0x50, 0xf0, 0xa8, 0xbd, 0xb9, 0x08, 0x26, 0x26, 0x1e, + 0x33, 0xa4, 0xa6, 0x5f, 0x87, 0x53, 0x2b, 0x8b, 0x25, 0x87, 0x5f, 0xe7, 0x8c, 0x35, 0x14, 0x00, 0xf1, 0xc9, 0x83, + 0xeb, 0xdd, 0xa4, 0x57, 0x4a, 0x3b, 0x28, 0x8d, 0x10, 0xdf, 0x55, 0xf8, 0xba, 0x0b, 0xc5, 0x57, 0xae, 0xba, 0xf7, + 0x35, 0x65, 0xc6, 0x05, 0xa3, 0xe7, 0x7c, 0x9a, 0x34, 0xae, 0xdd, 0xd0, 0x5d, 0x9d, 0x9f, 0xbc, 0x1f, 0x65, 0xde, + 0xc2, 0x14, 0xd8, 0x04, 0x54, 0xc1, 0x73, 0xef, 0xb5, 0x71, 0xa2, 0xfc, 0x9d, 0x79, 0xc4, 0x2b, 0x87, 0x59, 0x75, + 0x92, 0xfc, 0x5d, 0xff, 0xbb, 0xe0, 0xfa, 0x96, 0xc6, 0x09, 0x72, 0x57, 0x9d, 0x20, 0x13, 0xe5, 0xa6, 0x7a, 0xc3, + 0xed, 0xdf, 0x55, 0x20, 0x88, 0x53, 0x31, 0x7d, 0x54, 0x8e, 0xeb, 0x47, 0x0b, 0x54, 0x2a, 0x22, 0x3e, 0x55, 0xb9, + 0x2b, 0xd7, 0xa6, 0x86, 0x7a, 0x5c, 0x27, 0xb3, 0xd0, 0x34, 0x2b, 0x72, 0x29, 0x9b, 0x1e, 0x23, 0xd3, 0xec, 0x54, + 0x9b, 0xdf, 0xbd, 0xf6, 0x90, 0x8e, 0xa1, 0xb9, 0x58, 0xab, 0x05, 0xf7, 0xbb, 0x8a, 0xc2, 0xbb, 0x5e, 0x6c, 0xa4, + 0x32, 0xd4, 0xac, 0x47, 0xd1, 0xc7, 0x71, 0x9b, 0xb9, 0x3c, 0xca, 0xfe, 0xac, 0x01, 0x60, 0x3a, 0xc2, 0xa2, 0xbb, + 0xe9, 0x19, 0x7b, 0x02, 0x3d, 0x3d, 0x91, 0x41, 0xa2, 0xd7, 0x3a, 0x5f, 0xb5, 0x4a, 0x2c, 0x5d, 0x41, 0x60, 0xf7, + 0x86, 0x8c, 0x55, 0x49, 0xbb, 0xe5, 0xfa, 0xe5, 0x3c, 0x9f, 0xa7, 0x7c, 0x29, 0xcf, 0xa7, 0x66, 0xd1, 0xad, 0xb5, + 0xdd, 0x9b, 0x53, 0x43, 0xc5, 0x5c, 0xab, 0x9b, 0xfc, 0x81, 0xe9, 0x3a, 0x18, 0x6a, 0x11, 0x64, 0x56, 0xbb, 0xea, + 0x59, 0x59, 0x4e, 0xeb, 0x99, 0x1c, 0x53, 0xe1, 0x9b, 0x4a, 0x77, 0x88, 0x6e, 0x98, 0xaa, 0x99, 0x7e, 0x6a, 0x6c, + 0x0b, 0xd9, 0xe6, 0xf9, 0xe5, 0x28, 0x07, 0x4a, 0xcb, 0xfd, 0x65, 0xc2, 0xf0, 0xd3, 0xd5, 0xd5, 0x4f, 0x42, 0x4e, + 0x55, 0x1d, 0xbd, 0xc5, 0x4b, 0xdd, 0x33, 0x98, 0x96, 0xca, 0x89, 0x38, 0x61, 0xab, 0x07, 0x6f, 0xee, 0x5e, 0x01, + 0xcb, 0x09, 0x60, 0x77, 0xc2, 0x9c, 0xc6, 0x50, 0xd5, 0x06, 0xfe, 0x71, 0xf5, 0x60, 0xab, 0xf6, 0xf0, 0x8f, 0xfd, + 0x1f, 0x83, 0x1b, 0x1b, 0x1b, 0xdb, 0x78, 0xbb, 0x96, 0x08, 0xf2, 0x0a, 0x0f, 0xf4, 0xf1, 0xea, 0xa3, 0xa0, 0xe5, + 0x2a, 0xb1, 0xdd, 0x77, 0x28, 0x6c, 0x0d, 0xf2, 0x4d, 0xca, 0xa4, 0xe1, 0xbc, 0xe0, 0xd9, 0x54, 0xce, 0x50, 0xc8, + 0x6b, 0x3e, 0x0e, 0xda, 0x8e, 0xf0, 0x37, 0x70, 0x6a, 0xc7, 0xcb, 0x8b, 0x4f, 0xd0, 0x07, 0x3c, 0x5d, 0x29, 0x4d, + 0x45, 0x9c, 0x52, 0x6e, 0xd1, 0xe5, 0x3a, 0x0f, 0x46, 0x8a, 0x8b, 0x09, 0x2a, 0x1d, 0x77, 0x71, 0xe3, 0x6c, 0xe4, + 0xf4, 0x97, 0x78, 0x75, 0x91, 0x2e, 0x1f, 0x89, 0x6c, 0xd5, 0xd2, 0xfb, 0x5d, 0x9f, 0x6e, 0xdb, 0x53, 0xc6, 0x27, + 0xd9, 0x88, 0x0e, 0x66, 0x7c, 0x9c, 0x08, 0xaf, 0x4f, 0x8c, 0xf4, 0xdd, 0x22, 0x30, 0xdd, 0x1c, 0x9b, 0xfc, 0x70, + 0xbc, 0xde, 0x6c, 0xd6, 0xb8, 0xfd, 0x37, 0xce, 0x13, 0x67, 0x51, 0x62, 0x44, 0x65, 0xa1, 0xe1, 0x01, 0xad, 0x10, + 0x37, 0xef, 0x99, 0xc0, 0xb8, 0xec, 0x92, 0xa4, 0xb6, 0x1b, 0x08, 0x5c, 0xec, 0x71, 0xcc, 0x92, 0x91, 0xed, 0x41, + 0x79, 0xa0, 0x2f, 0x46, 0xd3, 0x2d, 0x60, 0x5a, 0x5e, 0x3b, 0x3b, 0x4f, 0x6d, 0xaf, 0x9a, 0x2a, 0x80, 0x59, 0xb2, + 0x3c, 0x3e, 0x45, 0xd6, 0xfd, 0x16, 0xba, 0x88, 0x01, 0x63, 0xe3, 0xca, 0x9c, 0xbb, 0x58, 0xb5, 0x22, 0xbe, 0xd1, + 0x44, 0x9a, 0xd4, 0x47, 0xd4, 0x77, 0x10, 0xd6, 0xea, 0x2a, 0x07, 0x09, 0xdc, 0x23, 0xef, 0x8e, 0xb8, 0xf4, 0xf4, + 0x99, 0xc5, 0xb8, 0x4a, 0xdf, 0x52, 0xd7, 0xe2, 0x9a, 0x61, 0xaf, 0xb8, 0x0f, 0xf6, 0x07, 0xc6, 0x2d, 0x62, 0x11, + 0x6f, 0xe7, 0xb5, 0x14, 0xd6, 0xc6, 0x1c, 0x68, 0x6e, 0xb8, 0xc1, 0xef, 0xac, 0x5a, 0x33, 0x30, 0xc3, 0x8c, 0x33, + 0x92, 0xdf, 0x8d, 0x7b, 0x55, 0x63, 0x47, 0xae, 0x02, 0x88, 0xbe, 0x05, 0x5d, 0x92, 0xc3, 0x2b, 0x59, 0xae, 0x3a, + 0x43, 0xfe, 0x0d, 0xd6, 0x59, 0x2f, 0x4e, 0xc0, 0x4c, 0x9a, 0xf2, 0x12, 0x13, 0x53, 0xc4, 0xe5, 0x66, 0x19, 0xf3, + 0x34, 0x7d, 0x16, 0xed, 0xe0, 0xe4, 0x46, 0x02, 0x47, 0xec, 0x1b, 0xcb, 0xd0, 0x4c, 0xd8, 0x88, 0x89, 0x34, 0x2a, + 0xa5, 0x84, 0xf7, 0xe5, 0x52, 0x4b, 0xfe, 0x32, 0x97, 0x57, 0x5f, 0x6e, 0x13, 0x1c, 0x90, 0xd7, 0xc0, 0x72, 0x68, + 0x1c, 0xb7, 0x0c, 0x24, 0x62, 0x31, 0x20, 0x46, 0xad, 0xca, 0xe5, 0x64, 0x54, 0x27, 0xf3, 0x15, 0x72, 0xa1, 0x22, + 0x0f, 0x6e, 0x09, 0x94, 0xfc, 0x39, 0xa6, 0x0e, 0x66, 0xa5, 0x76, 0xd3, 0x62, 0x93, 0xe4, 0x3d, 0x33, 0x20, 0xb9, + 0xfa, 0x1a, 0x1e, 0x1a, 0xbf, 0x78, 0x65, 0x4e, 0x09, 0x5f, 0x94, 0xb1, 0xb4, 0x34, 0xe6, 0xd2, 0xbf, 0x90, 0xf7, + 0x69, 0x25, 0x60, 0xbf, 0x81, 0x98, 0x32, 0x70, 0x89, 0x8d, 0x0b, 0x92, 0xf2, 0x5a, 0x9e, 0xb2, 0xfb, 0x1a, 0xca, + 0x77, 0xc9, 0xa4, 0xab, 0x54, 0xd6, 0x35, 0x56, 0xdd, 0x6f, 0x73, 0x96, 0x5f, 0x1e, 0x30, 0xcc, 0x4d, 0x46, 0x83, + 0x6c, 0xc9, 0xcc, 0xa6, 0xfc, 0x6a, 0xef, 0xc6, 0xaf, 0x3c, 0x94, 0x74, 0xa8, 0x56, 0xe9, 0xe6, 0xa5, 0x1b, 0x8e, + 0x71, 0xe3, 0x86, 0x23, 0x80, 0x8d, 0x61, 0xa7, 0x8a, 0xd4, 0x3a, 0xff, 0x7d, 0x39, 0xfc, 0x44, 0x7b, 0x6d, 0xa0, + 0x77, 0xdd, 0x60, 0x65, 0x7a, 0xfa, 0x0d, 0xa8, 0x1a, 0x59, 0x42, 0x37, 0xa1, 0x8a, 0xc9, 0x48, 0x94, 0x98, 0xae, + 0x52, 0x1e, 0xf5, 0x35, 0xe2, 0x1c, 0xc4, 0x0d, 0xe5, 0x2f, 0xfe, 0x35, 0xbc, 0x3c, 0x0e, 0xd0, 0x88, 0x5a, 0x8c, + 0xb3, 0x94, 0xb7, 0xc6, 0xd1, 0x34, 0x4e, 0x2e, 0x83, 0x79, 0xdc, 0x9a, 0x66, 0x69, 0x56, 0xcc, 0x80, 0x2b, 0xbd, + 0xe2, 0x12, 0x6c, 0xf8, 0x69, 0x6b, 0x1e, 0x7b, 0xcf, 0x59, 0x72, 0xc6, 0x78, 0x3c, 0x8c, 0x3c, 0x7b, 0x3f, 0x07, + 0xf1, 0x60, 0xbd, 0x8e, 0xf2, 0x3c, 0x3b, 0xb7, 0xbd, 0x77, 0xd9, 0x09, 0x30, 0xad, 0xf7, 0xe6, 0xe2, 0xf2, 0x94, + 0xa5, 0xde, 0xfb, 0x93, 0x79, 0xca, 0xe7, 0x5e, 0x11, 0xa5, 0x45, 0xab, 0x60, 0x79, 0x3c, 0x06, 0x35, 0x91, 0x64, + 0x79, 0x0b, 0xf3, 0x9f, 0xa7, 0x2c, 0x48, 0xe2, 0xd3, 0x09, 0xb7, 0x46, 0x51, 0xfe, 0xa9, 0xd7, 0x6a, 0xcd, 0xf2, + 0x78, 0x1a, 0xe5, 0x97, 0x2d, 0x6a, 0x11, 0x7c, 0xd1, 0xde, 0x8e, 0xbe, 0x1a, 0xdf, 0xef, 0xf1, 0x1c, 0xfa, 0xc6, + 0x48, 0xc5, 0x00, 0x84, 0x8f, 0xb5, 0xbd, 0xd3, 0x9e, 0x16, 0x6b, 0xe2, 0x44, 0x29, 0x4a, 0x79, 0x79, 0xec, 0x7d, + 0x64, 0x00, 0xb7, 0x7f, 0xc2, 0x53, 0x0f, 0x7c, 0x39, 0x9e, 0xa5, 0x8b, 0xe1, 0x3c, 0x2f, 0x60, 0x80, 0x59, 0x16, + 0xa7, 0x9c, 0xe5, 0xbd, 0x93, 0x2c, 0x07, 0xb2, 0xb5, 0xf2, 0x68, 0x14, 0xcf, 0x8b, 0xe0, 0xfe, 0xec, 0xa2, 0x87, + 0xb6, 0xc2, 0x69, 0x9e, 0xcd, 0xd3, 0x91, 0x9c, 0x2b, 0x4e, 0x61, 0x63, 0xc4, 0xdc, 0xac, 0xa0, 0x2f, 0xa1, 0x00, + 0x7c, 0x29, 0x8b, 0xf2, 0xd6, 0x29, 0x76, 0x46, 0x43, 0xbf, 0x3d, 0x62, 0xa7, 0x5e, 0x7e, 0x7a, 0x12, 0x39, 0x9d, + 0xee, 0x43, 0x4f, 0xfd, 0xf3, 0x77, 0x5c, 0x30, 0xdc, 0x57, 0x16, 0x77, 0xda, 0xed, 0x7f, 0x70, 0x7b, 0x8d, 0x59, + 0x08, 0xa0, 0xa0, 0x33, 0xbb, 0xb0, 0x8a, 0x2c, 0x81, 0xf5, 0x59, 0xd5, 0xb3, 0x37, 0x03, 0xbf, 0x29, 0x4e, 0x4f, + 0x83, 0xee, 0xec, 0xa2, 0x44, 0xec, 0x02, 0x91, 0x90, 0x29, 0x91, 0x94, 0x6f, 0x8b, 0x3f, 0x0a, 0xf1, 0xa3, 0xd5, + 0x10, 0x77, 0x15, 0xc4, 0x15, 0xd5, 0x5b, 0x23, 0xd8, 0x07, 0x44, 0xfe, 0x4e, 0x21, 0x00, 0x99, 0x80, 0x13, 0x98, + 0x2b, 0x38, 0xe8, 0xe5, 0x0f, 0x83, 0xd1, 0x5d, 0x0d, 0xc6, 0xa3, 0xdb, 0xc0, 0xc8, 0xd3, 0xd1, 0xa2, 0xbe, 0xae, + 0x1d, 0x70, 0x4e, 0x7b, 0x13, 0x86, 0xfc, 0x14, 0x74, 0xf1, 0xf9, 0x3c, 0x1e, 0xf1, 0x89, 0x78, 0x24, 0x76, 0x3e, + 0x17, 0x75, 0x3b, 0xed, 0xb6, 0x78, 0x2f, 0x40, 0xa1, 0x05, 0x1d, 0x1f, 0x1b, 0x00, 0x13, 0x7d, 0xb8, 0xee, 0x23, + 0x36, 0x5f, 0xdf, 0xfa, 0xa5, 0x1a, 0xef, 0x5c, 0xe5, 0x0d, 0x0a, 0x15, 0xa1, 0xbe, 0xd9, 0x82, 0x19, 0x6f, 0xf9, + 0x5e, 0x47, 0x1f, 0x54, 0xf5, 0xbf, 0x66, 0xa4, 0xf5, 0x02, 0xee, 0x99, 0xb9, 0x40, 0xbd, 0x74, 0x0f, 0x43, 0x52, + 0xad, 0x96, 0x0b, 0x7a, 0x83, 0x61, 0x08, 0x89, 0x0e, 0x04, 0x9d, 0xbc, 0x5f, 0xd0, 0x37, 0x35, 0x32, 0x37, 0x28, + 0x9c, 0xcc, 0x85, 0x2d, 0x9f, 0x69, 0xb9, 0x0e, 0x4a, 0x1a, 0xbc, 0xec, 0x0f, 0x4c, 0x36, 0x00, 0xe9, 0x4d, 0xa1, + 0xae, 0x1f, 0x42, 0xe1, 0x4a, 0x29, 0x47, 0x6a, 0x7a, 0xd3, 0x15, 0x7d, 0x58, 0x95, 0x98, 0x32, 0x92, 0x8f, 0x06, + 0xff, 0x0a, 0xc3, 0xde, 0xd1, 0x8e, 0x65, 0x91, 0xcd, 0xf3, 0x21, 0x45, 0xea, 0x96, 0x3d, 0x7e, 0x9b, 0x14, 0xae, + 0xed, 0x31, 0x2d, 0xe7, 0xd1, 0x0d, 0xae, 0x7d, 0xe4, 0x80, 0xd3, 0x21, 0x88, 0xb8, 0x63, 0x20, 0xa3, 0x1c, 0x0a, + 0x42, 0x54, 0x5d, 0x23, 0xca, 0x77, 0xa3, 0xfb, 0x97, 0xfe, 0x3c, 0x8d, 0x41, 0xd2, 0x7d, 0x8c, 0x47, 0x74, 0xef, + 0x24, 0x1e, 0xd1, 0x41, 0x44, 0x8b, 0x12, 0x8f, 0x30, 0xb2, 0x0d, 0x05, 0xea, 0x3b, 0x2c, 0xf0, 0x2c, 0x13, 0x59, + 0xec, 0x96, 0x8d, 0x87, 0x09, 0x86, 0xaa, 0x1c, 0x65, 0xd3, 0x28, 0x4e, 0x03, 0xfc, 0x3e, 0x88, 0xa7, 0x47, 0x0c, + 0xb0, 0x8b, 0x07, 0x3f, 0x99, 0xcc, 0x45, 0xeb, 0xb8, 0xfe, 0xaf, 0x20, 0x47, 0xa8, 0x7f, 0x29, 0xfd, 0x30, 0x0d, + 0x97, 0x3a, 0xe6, 0xad, 0x97, 0x82, 0xec, 0xe1, 0xca, 0x66, 0x65, 0x14, 0xe7, 0xd8, 0xe5, 0xf4, 0xa3, 0xdf, 0xea, + 0x04, 0x1d, 0xed, 0xba, 0xd6, 0x6e, 0xa3, 0x8a, 0x5c, 0x16, 0x79, 0xa3, 0x91, 0x60, 0xd0, 0xcf, 0x02, 0xce, 0xea, + 0x5d, 0xc3, 0xea, 0x49, 0xbe, 0xc4, 0x00, 0xce, 0x69, 0xea, 0xd4, 0x80, 0xa0, 0xb3, 0x80, 0x6b, 0xa6, 0x72, 0xcb, + 0x88, 0x94, 0xd2, 0x63, 0xda, 0xc0, 0xf5, 0xbb, 0x44, 0x78, 0x6f, 0xa8, 0x9e, 0x02, 0xa5, 0x58, 0x6e, 0x74, 0xbc, + 0x2b, 0x76, 0xbc, 0x45, 0x3c, 0x16, 0xda, 0xb0, 0x05, 0x6d, 0xeb, 0x2f, 0x43, 0xa0, 0xd2, 0xa7, 0xd0, 0x5e, 0x5f, + 0x38, 0x2a, 0xb1, 0x3e, 0x87, 0xb9, 0xf6, 0x85, 0xd6, 0xa3, 0x1b, 0xf9, 0xf6, 0xde, 0xfa, 0x82, 0x97, 0xbb, 0x5b, + 0xa2, 0xf7, 0xde, 0x71, 0x59, 0x90, 0x82, 0x32, 0x03, 0x69, 0xd5, 0x14, 0xa2, 0x0e, 0x86, 0xa5, 0xf4, 0x5d, 0x1c, + 0x37, 0xd7, 0x46, 0x97, 0x88, 0x18, 0x4b, 0xb6, 0x2b, 0x30, 0x5d, 0x29, 0xca, 0x61, 0x4f, 0xea, 0x84, 0x94, 0x42, + 0xe4, 0x60, 0xf4, 0x96, 0xa1, 0x38, 0x46, 0x08, 0xfa, 0xeb, 0x8b, 0xb8, 0x0c, 0xd7, 0x17, 0x59, 0x79, 0x0c, 0x96, + 0x09, 0x42, 0x95, 0xab, 0xcf, 0xbb, 0xc0, 0xc4, 0x22, 0xc8, 0x62, 0xd1, 0x08, 0x38, 0x2d, 0x2b, 0x6d, 0xab, 0x2f, + 0xa0, 0x01, 0x0f, 0x10, 0x0b, 0xc0, 0x76, 0xa3, 0x5e, 0x0c, 0x70, 0x11, 0xad, 0xf7, 0x60, 0xa0, 0xdd, 0x2d, 0xd1, + 0x08, 0xf0, 0xca, 0x11, 0xe4, 0x0a, 0x2d, 0x4c, 0xc7, 0x31, 0x51, 0x1b, 0xc5, 0x67, 0x9a, 0x74, 0x94, 0x9b, 0xbc, + 0xb7, 0x9b, 0x44, 0x27, 0x2c, 0x81, 0x21, 0x8b, 0xab, 0xab, 0x36, 0x8c, 0x24, 0x5e, 0xad, 0xdd, 0x38, 0x9d, 0xcd, + 0xe5, 0x67, 0xb6, 0x60, 0xe2, 0x0e, 0x9e, 0x7c, 0xe2, 0x2d, 0x80, 0xbe, 0x3a, 0xca, 0x0b, 0xe4, 0x00, 0x00, 0x22, + 0x9d, 0x22, 0x20, 0x74, 0x15, 0x5b, 0x40, 0x69, 0x3c, 0x5a, 0x2e, 0xd3, 0x98, 0xe5, 0xf0, 0x02, 0xdb, 0x72, 0x86, + 0xcf, 0x29, 0x3c, 0x4f, 0xe3, 0x14, 0x1f, 0x0b, 0x7c, 0x8c, 0x2e, 0xf0, 0x31, 0x2b, 0xed, 0xbf, 0xcb, 0x0a, 0x58, + 0x9a, 0x00, 0xd9, 0xd5, 0x15, 0xc8, 0x7b, 0x4d, 0x82, 0xdd, 0x2d, 0x20, 0x16, 0x72, 0x8a, 0xf8, 0xe8, 0x0a, 0x33, + 0xc9, 0xc8, 0x8a, 0x59, 0x4b, 0x94, 0x5b, 0xa4, 0x55, 0x43, 0x70, 0xba, 0x72, 0xa7, 0x61, 0x3c, 0x78, 0x32, 0xbd, + 0xe4, 0x09, 0xbe, 0xb8, 0xb6, 0x25, 0xbe, 0x8a, 0x21, 0x88, 0x42, 0x8f, 0x88, 0xa1, 0x2e, 0xe3, 0xf2, 0x7b, 0x37, + 0x71, 0x68, 0xe3, 0x2c, 0x60, 0xbf, 0xa1, 0x16, 0xe0, 0x51, 0x9c, 0x88, 0xc6, 0x2b, 0xf0, 0x69, 0xe4, 0x09, 0x12, + 0x3a, 0xbb, 0x5b, 0x15, 0x6c, 0x00, 0xfc, 0x50, 0xdc, 0xb2, 0x76, 0x44, 0x0e, 0xa4, 0x2d, 0xca, 0xe9, 0xec, 0x5c, + 0x6e, 0x69, 0x19, 0xd9, 0x15, 0xb1, 0x72, 0x8d, 0x2a, 0xe5, 0x2c, 0xda, 0xe3, 0x28, 0x5d, 0xd5, 0x14, 0xa0, 0x9f, + 0x31, 0x36, 0xf2, 0x6c, 0x0b, 0x64, 0xa9, 0x78, 0xfe, 0x98, 0xb0, 0x33, 0x26, 0xbf, 0x94, 0xa2, 0x07, 0xd1, 0x95, + 0x43, 0x50, 0xc9, 0x5c, 0x5e, 0xe2, 0x94, 0xec, 0xa9, 0x70, 0x94, 0x94, 0xa8, 0x23, 0xe2, 0xd9, 0xc6, 0xa0, 0xcd, + 0x39, 0xda, 0xf5, 0x61, 0xbd, 0x0e, 0x58, 0xbb, 0xb6, 0x80, 0x97, 0xec, 0xb8, 0xdb, 0x91, 0x83, 0x01, 0xd8, 0x78, + 0x0c, 0xdb, 0x45, 0x45, 0x96, 0xb5, 0x2c, 0x10, 0x50, 0x81, 0x53, 0xea, 0xd9, 0xa2, 0x85, 0x5d, 0xb5, 0xd5, 0x4f, + 0x92, 0x38, 0x49, 0x36, 0xfc, 0xb4, 0x62, 0x2e, 0xe0, 0x8e, 0x75, 0x11, 0x69, 0x6d, 0xc8, 0x37, 0xfb, 0x5f, 0xfe, + 0xf9, 0xbf, 0xff, 0x67, 0x18, 0x98, 0xfa, 0xb9, 0xa5, 0x75, 0x7d, 0xab, 0xff, 0x06, 0xad, 0xe6, 0xe9, 0x2d, 0xed, + 0xfe, 0xf6, 0x1f, 0xfe, 0x0b, 0x34, 0xa3, 0x1b, 0x39, 0x6e, 0x79, 0x4c, 0x10, 0x0d, 0xd1, 0x08, 0xfa, 0x2c, 0x90, + 0x6a, 0x83, 0x5c, 0x3b, 0xd3, 0x3f, 0x21, 0xd8, 0x05, 0xcf, 0x66, 0x37, 0x82, 0x83, 0x50, 0x0f, 0x93, 0xac, 0x60, + 0x1a, 0x1e, 0x21, 0x6b, 0x3f, 0x0f, 0x20, 0x9a, 0x6b, 0x06, 0x5c, 0x5e, 0x58, 0x7a, 0x1c, 0xb1, 0x3c, 0xab, 0xc6, + 0x69, 0xac, 0x5e, 0xc1, 0x38, 0xa1, 0x43, 0x71, 0x05, 0x58, 0x2f, 0xf1, 0x04, 0x0f, 0x24, 0x10, 0xdc, 0xfa, 0x57, + 0xbe, 0x56, 0x0f, 0xa6, 0xf9, 0x53, 0x8c, 0x25, 0x42, 0x29, 0x6a, 0x04, 0xf8, 0x09, 0x72, 0xea, 0xa3, 0x7e, 0x8e, + 0x2e, 0xf4, 0x33, 0xca, 0x32, 0x31, 0x01, 0xe8, 0xa2, 0x29, 0x9a, 0x19, 0xe6, 0x0c, 0x22, 0x0d, 0xa0, 0xf2, 0x23, + 0x8d, 0x6c, 0x12, 0x21, 0xbc, 0x3e, 0x62, 0xd2, 0x25, 0x5e, 0xb1, 0x99, 0xe7, 0xec, 0x63, 0x92, 0x9d, 0x63, 0x70, + 0x0a, 0x89, 0x74, 0x5d, 0x7d, 0x69, 0xf9, 0xbe, 0x75, 0x4d, 0xf5, 0x04, 0x76, 0x01, 0xd8, 0x92, 0x3c, 0xd4, 0x54, + 0x6e, 0x20, 0xb5, 0x7a, 0x48, 0xc4, 0x72, 0x75, 0x8d, 0x44, 0x1b, 0x4b, 0x25, 0xd6, 0xf2, 0x11, 0x91, 0xf6, 0xda, + 0x15, 0x53, 0xdc, 0x3e, 0x0f, 0xc2, 0x76, 0xcd, 0x44, 0x58, 0x75, 0xeb, 0x4c, 0xc7, 0x6e, 0x70, 0x03, 0x65, 0x3e, + 0x13, 0xeb, 0xd5, 0x80, 0xdc, 0x01, 0x06, 0x0f, 0x34, 0x80, 0xd0, 0x11, 0x4f, 0x84, 0x84, 0x0f, 0x68, 0x2d, 0xa4, + 0xb8, 0xaf, 0x41, 0xe4, 0x59, 0xff, 0xe7, 0x7f, 0x1e, 0xd2, 0x1c, 0xd8, 0x26, 0xae, 0xf4, 0x2b, 0x99, 0x23, 0x27, + 0xb9, 0x52, 0xbf, 0xaf, 0xf0, 0x82, 0x1d, 0x8d, 0x82, 0x4a, 0xb6, 0x20, 0x9b, 0x80, 0x93, 0x4d, 0x60, 0xea, 0x61, + 0xf4, 0x32, 0xb2, 0xd5, 0xfa, 0xf6, 0x23, 0xf6, 0xab, 0x69, 0x4c, 0x2e, 0x35, 0x66, 0x2c, 0xad, 0x59, 0xa9, 0x31, + 0xf1, 0xfb, 0x29, 0x0d, 0x8d, 0x19, 0x5f, 0xab, 0x31, 0x91, 0x76, 0x7d, 0x1c, 0x39, 0xb4, 0x37, 0x31, 0x94, 0x62, + 0x68, 0x70, 0x8e, 0xb6, 0x09, 0xf8, 0xcf, 0xc3, 0x4f, 0xd2, 0x34, 0x21, 0xc8, 0x31, 0x96, 0x20, 0x8d, 0x2d, 0xb2, + 0x4b, 0x04, 0x76, 0xc7, 0xa5, 0xde, 0xf8, 0xf8, 0x68, 0x4c, 0xc0, 0xdd, 0xc5, 0x98, 0xa1, 0x58, 0x3e, 0xde, 0xc2, + 0x21, 0xc4, 0x7e, 0x29, 0xe9, 0x19, 0x90, 0xda, 0xa2, 0x71, 0xbc, 0x85, 0x08, 0x4a, 0x05, 0xf6, 0xdb, 0x37, 0x07, + 0x87, 0xb6, 0x77, 0x92, 0x8d, 0x2e, 0x03, 0x1b, 0x1c, 0x0a, 0x30, 0x3e, 0x5c, 0x9f, 0x4f, 0x58, 0xea, 0x28, 0x93, + 0x3e, 0x4b, 0xc0, 0xa5, 0xc9, 0x4e, 0xc5, 0x37, 0x14, 0x9a, 0x01, 0x75, 0x20, 0x26, 0x7d, 0x64, 0x71, 0x6f, 0x97, + 0x8b, 0xef, 0x8f, 0xf2, 0x1c, 0x1f, 0xf7, 0x30, 0xa5, 0x60, 0x77, 0x0b, 0x1e, 0xf0, 0xe5, 0x00, 0xd5, 0x91, 0x7e, + 0x13, 0x70, 0x16, 0xe2, 0x7d, 0x0b, 0xdb, 0x6f, 0xa9, 0xbe, 0x08, 0xc5, 0x9e, 0x64, 0x35, 0x6d, 0x75, 0x57, 0xa6, + 0x1c, 0x8d, 0x3d, 0x42, 0x4b, 0x8d, 0xac, 0x6e, 0x20, 0x05, 0x1f, 0xe9, 0x12, 0xa1, 0xfd, 0x8d, 0x22, 0x1a, 0xa5, + 0xd2, 0xd5, 0xb2, 0x0a, 0x27, 0x24, 0x2a, 0x8a, 0xc9, 0xe0, 0x27, 0x81, 0x7f, 0x6c, 0x7e, 0x2f, 0x4c, 0x7c, 0xda, + 0x47, 0x23, 0x79, 0xf4, 0x57, 0xef, 0x23, 0xf3, 0x2e, 0x8f, 0xa9, 0xa5, 0x72, 0x4e, 0x31, 0x6a, 0x82, 0x4e, 0x7c, + 0x5b, 0x45, 0x21, 0xc0, 0x3c, 0x49, 0xa2, 0x59, 0xc1, 0x02, 0xf5, 0x20, 0xfd, 0x54, 0x74, 0x77, 0x97, 0x03, 0x03, + 0xa6, 0x19, 0x53, 0xf2, 0xc9, 0xc2, 0x74, 0x64, 0x1f, 0x80, 0x23, 0x8b, 0x49, 0xf8, 0xad, 0x08, 0x94, 0x6f, 0x1a, + 0x24, 0x6c, 0xcc, 0x4b, 0x8e, 0xb7, 0xbc, 0x17, 0x2a, 0x72, 0xe0, 0x77, 0x77, 0xc0, 0xb9, 0xb5, 0x7c, 0xfc, 0xff, + 0xb6, 0xb1, 0x47, 0x41, 0x0a, 0xce, 0x28, 0x5d, 0xfb, 0xc0, 0x2b, 0x75, 0x00, 0x91, 0xf9, 0xbe, 0x30, 0x26, 0x1a, + 0x32, 0x8c, 0xaa, 0x94, 0x3c, 0x07, 0xb1, 0xed, 0xf1, 0xdc, 0x6c, 0x07, 0xe2, 0x76, 0x29, 0xb4, 0xb2, 0xec, 0xdc, + 0x6f, 0xbb, 0xd2, 0x05, 0x58, 0x6e, 0xac, 0x22, 0x24, 0xf5, 0xb7, 0x25, 0x0a, 0x19, 0xb1, 0x9c, 0x52, 0x68, 0x9a, + 0x85, 0xe8, 0x61, 0xe2, 0xb4, 0x1c, 0xe5, 0xb9, 0xd5, 0x50, 0x2c, 0x69, 0xc7, 0x1f, 0xd1, 0x8e, 0x27, 0x19, 0x36, + 0x58, 0x88, 0xb9, 0x87, 0x51, 0x32, 0x74, 0x10, 0x00, 0xab, 0x65, 0x3d, 0x02, 0x6a, 0xba, 0x2a, 0xd2, 0xe0, 0x3f, + 0x44, 0xe2, 0x96, 0x42, 0xe2, 0xad, 0xa0, 0xd2, 0xf1, 0xa0, 0x2c, 0x7b, 0xe7, 0xcc, 0x39, 0xfa, 0x5d, 0x5e, 0x1a, + 0x10, 0x77, 0x45, 0xf5, 0xf7, 0xf6, 0xda, 0xa5, 0x3b, 0xf0, 0x7e, 0x30, 0x3e, 0x62, 0x66, 0x2b, 0x86, 0xb6, 0x3d, + 0x58, 0x86, 0x1f, 0x42, 0xec, 0xfb, 0xca, 0xb1, 0xd1, 0xb2, 0xa4, 0x9a, 0xcb, 0x16, 0xf1, 0x97, 0x8d, 0xdd, 0x44, + 0xc8, 0xfb, 0xfb, 0xeb, 0x22, 0x17, 0xdf, 0xdc, 0x1e, 0xb9, 0x60, 0x77, 0x8c, 0x5c, 0x7c, 0xf3, 0x27, 0x47, 0x2e, + 0xbe, 0x6f, 0x46, 0x2e, 0x7e, 0xfb, 0x9c, 0xc8, 0x45, 0x9e, 0x9d, 0x17, 0x61, 0x47, 0x9e, 0x94, 0x83, 0xcc, 0xf9, + 0xfb, 0x84, 0x30, 0x60, 0xa2, 0x46, 0x00, 0x83, 0x22, 0x16, 0x22, 0xb9, 0x0f, 0x24, 0xbb, 0x8c, 0x17, 0xb4, 0x75, + 0x16, 0x5d, 0xeb, 0xbe, 0xba, 0x36, 0x04, 0x1e, 0x9b, 0xab, 0x2f, 0xbc, 0x75, 0x55, 0x44, 0x21, 0xa0, 0xef, 0x7e, + 0xea, 0x8e, 0xdd, 0x4d, 0x95, 0xbe, 0x65, 0x8e, 0xd0, 0x53, 0x51, 0x79, 0xc1, 0x3e, 0x0b, 0xfb, 0xdf, 0x1d, 0x75, + 0x7a, 0xdb, 0x9d, 0x29, 0xf4, 0x06, 0x2d, 0x0a, 0x6f, 0xed, 0xde, 0xf6, 0x36, 0xbe, 0x9d, 0xab, 0xb7, 0x2e, 0xbe, + 0xc5, 0xea, 0x6d, 0x07, 0xdf, 0x86, 0xea, 0xed, 0x01, 0xbe, 0x8d, 0xd4, 0xdb, 0x43, 0x7c, 0x3b, 0xb3, 0xcb, 0x23, + 0xae, 0x81, 0x7b, 0x08, 0x7c, 0x45, 0xc6, 0x7e, 0xa0, 0xca, 0x60, 0xd3, 0xe2, 0x75, 0xbb, 0xe8, 0x34, 0x88, 0x3d, + 0xe1, 0x14, 0x05, 0xb9, 0x77, 0x0e, 0x92, 0x3f, 0xa0, 0xec, 0xb2, 0xa7, 0xf8, 0xdd, 0x05, 0xf0, 0x21, 0x0e, 0xe3, + 0x29, 0x53, 0x1f, 0xa0, 0x55, 0x58, 0x83, 0x1d, 0x79, 0xd4, 0x1e, 0x94, 0x3d, 0xbd, 0x4e, 0x22, 0x60, 0xa2, 0x4e, + 0xef, 0x69, 0xe5, 0xaa, 0x3a, 0x31, 0x5d, 0x4b, 0xaf, 0xf0, 0x35, 0x7a, 0xc4, 0x70, 0xa3, 0xc7, 0x60, 0x21, 0xb5, + 0x2e, 0xc0, 0xf1, 0x5a, 0xa9, 0x5b, 0x10, 0x22, 0xad, 0x4d, 0x08, 0x27, 0xfd, 0x76, 0x18, 0x9d, 0xea, 0xe7, 0x57, + 0x60, 0xf0, 0x46, 0xa7, 0xec, 0x36, 0x3d, 0x43, 0x20, 0x9a, 0x3a, 0x46, 0x01, 0x41, 0xf6, 0x10, 0x2c, 0x0d, 0x3a, + 0x80, 0x52, 0xc7, 0x20, 0x75, 0xea, 0x5a, 0x87, 0xa6, 0xaf, 0x17, 0x01, 0x45, 0xab, 0x82, 0x5d, 0xb0, 0xbb, 0xa9, + 0x54, 0x50, 0x18, 0x2a, 0xb0, 0xe0, 0x46, 0x55, 0xa4, 0x7d, 0xe4, 0x6b, 0x15, 0x92, 0xa5, 0x74, 0x91, 0x19, 0xcd, + 0xd7, 0xa1, 0xfc, 0x65, 0xf1, 0xf8, 0x45, 0x67, 0x88, 0x7f, 0xa4, 0xf0, 0xfd, 0x62, 0x3c, 0x1e, 0xdf, 0xa8, 0x9b, + 0xbe, 0x18, 0x8d, 0x59, 0x97, 0xed, 0xf4, 0x30, 0xd2, 0xdb, 0x92, 0xe2, 0xb0, 0x53, 0x12, 0xed, 0x16, 0x77, 0x6b, + 0x8c, 0x92, 0x13, 0xd4, 0xd5, 0xdd, 0x95, 0x58, 0x09, 0x54, 0x59, 0x80, 0xf0, 0x3e, 0x8d, 0xd3, 0xa0, 0x5d, 0xfa, + 0x67, 0x52, 0xea, 0x7f, 0xf1, 0xe8, 0xd1, 0xa3, 0xd2, 0x1f, 0xa9, 0xb7, 0xf6, 0x68, 0x54, 0xfa, 0xc3, 0x85, 0x46, + 0xa3, 0xdd, 0x1e, 0x8f, 0x4b, 0x3f, 0x56, 0x05, 0xdb, 0xdd, 0xe1, 0x68, 0xbb, 0x5b, 0xfa, 0xe7, 0x46, 0x8b, 0xd2, + 0x67, 0xf2, 0x2d, 0x67, 0xa3, 0x5a, 0xb8, 0xf8, 0x61, 0x1b, 0x2a, 0x05, 0xa3, 0x2d, 0xd0, 0xc9, 0x13, 0x8f, 0x41, + 0x34, 0xe7, 0x59, 0x79, 0x0c, 0xb2, 0x9d, 0x81, 0x7c, 0x1e, 0x4b, 0xd9, 0x2e, 0xbe, 0xef, 0x8a, 0x12, 0xfd, 0x37, + 0x53, 0xa2, 0x23, 0x33, 0x93, 0x34, 0x67, 0xa4, 0x07, 0x9a, 0xd5, 0xc8, 0x59, 0x54, 0xfd, 0x5b, 0xc8, 0x2a, 0x61, + 0x8f, 0xd2, 0x06, 0x5b, 0x0a, 0x19, 0xff, 0xed, 0x75, 0x32, 0xfe, 0xbb, 0xdb, 0x65, 0xfc, 0xc9, 0xdd, 0x44, 0xfc, + 0x77, 0x7f, 0xb2, 0x88, 0xff, 0xd6, 0x14, 0xf1, 0x42, 0x88, 0x5d, 0x82, 0xf5, 0x4a, 0x66, 0xeb, 0x49, 0x76, 0xd1, + 0xc2, 0x2d, 0x91, 0xdb, 0x24, 0x3d, 0xd7, 0xef, 0x24, 0xfc, 0x57, 0xe4, 0xff, 0xa8, 0xc1, 0x8c, 0x8f, 0xc5, 0xf2, + 0xec, 0xf4, 0x34, 0x61, 0x4a, 0xc6, 0x1b, 0x15, 0x64, 0x0e, 0xbf, 0x49, 0x43, 0xfb, 0x0d, 0x38, 0xa8, 0x46, 0xc9, + 0x78, 0x0c, 0x45, 0xe3, 0xb1, 0xad, 0xf2, 0x63, 0x41, 0x9e, 0x51, 0xab, 0xd7, 0xb5, 0x12, 0x6a, 0xf5, 0xf5, 0xd7, + 0x66, 0x99, 0x59, 0x20, 0x23, 0x51, 0xa6, 0x31, 0x21, 0x6b, 0x46, 0x71, 0x81, 0x7b, 0xb0, 0xfa, 0xb8, 0x2d, 0xda, + 0x2b, 0x53, 0x50, 0x2a, 0xf1, 0x10, 0xbf, 0x9a, 0xd2, 0xfc, 0x90, 0x88, 0xc8, 0x65, 0x5e, 0x46, 0xae, 0x3a, 0xef, + 0x34, 0xbe, 0x59, 0x57, 0x9d, 0x71, 0xc2, 0xe2, 0xcb, 0x7c, 0x86, 0xc7, 0x97, 0x2f, 0x46, 0xce, 0x25, 0xd8, 0xb1, + 0x71, 0xf1, 0x26, 0x6d, 0xe4, 0x89, 0x09, 0xb0, 0xc3, 0xd0, 0xc4, 0xb4, 0x14, 0x04, 0xab, 0x12, 0xe6, 0xab, 0xca, + 0x9e, 0xd1, 0x49, 0xa6, 0x13, 0xe1, 0x90, 0xfd, 0x1a, 0x59, 0x02, 0x73, 0x30, 0xa9, 0x0b, 0xe9, 0xe3, 0xe5, 0x22, + 0xc9, 0xe2, 0x4c, 0x7e, 0xe5, 0x9a, 0xa2, 0xff, 0x0b, 0xa9, 0x3f, 0xe4, 0xf1, 0x7b, 0xd5, 0x13, 0x03, 0xee, 0x62, + 0x86, 0x51, 0xa9, 0x82, 0xec, 0x40, 0xb8, 0x19, 0x7e, 0x92, 0x47, 0x0c, 0xa1, 0x62, 0xd9, 0x15, 0xf5, 0xf0, 0x13, + 0x28, 0xd5, 0x97, 0x21, 0x6b, 0x5f, 0x11, 0x6c, 0xf0, 0x00, 0x7e, 0xdd, 0x9f, 0xa3, 0x36, 0xc8, 0xe6, 0xdc, 0x71, + 0xa8, 0x95, 0xe3, 0x96, 0x5e, 0x77, 0x07, 0x3c, 0x4a, 0xd7, 0x17, 0xdf, 0xfd, 0x71, 0x74, 0x67, 0x89, 0xef, 0x75, + 0xa1, 0xf3, 0xa5, 0xef, 0x70, 0x69, 0x12, 0xe3, 0x87, 0x42, 0x04, 0xa2, 0xc6, 0x5d, 0x11, 0xb5, 0x88, 0xcd, 0x77, + 0x5f, 0xb9, 0x6f, 0x06, 0x61, 0xdd, 0x55, 0x1c, 0x2c, 0xe3, 0x64, 0xf5, 0x42, 0x6c, 0x2b, 0xac, 0x9a, 0x65, 0x70, + 0x6e, 0xd1, 0x99, 0xc5, 0xb9, 0x11, 0x77, 0xae, 0x6d, 0x83, 0x52, 0x05, 0x9e, 0x45, 0xf4, 0xf8, 0x12, 0x63, 0xa4, + 0xc2, 0xf7, 0x55, 0x40, 0xd7, 0xbd, 0x4e, 0x03, 0x72, 0xf4, 0x47, 0x35, 0xa3, 0xab, 0x2a, 0x55, 0x50, 0x9a, 0xa7, + 0x04, 0x06, 0x32, 0x14, 0x00, 0x86, 0x35, 0x4e, 0x85, 0xde, 0x82, 0x69, 0x48, 0x00, 0x6b, 0x8f, 0x0c, 0xdd, 0x12, + 0x5b, 0x81, 0x2d, 0xa4, 0x05, 0x28, 0x3d, 0xec, 0xb0, 0x67, 0xd5, 0x40, 0x4f, 0x97, 0xe3, 0xc6, 0x37, 0x39, 0x69, + 0x97, 0xc7, 0x7e, 0x71, 0xee, 0xc1, 0x3f, 0xeb, 0xcb, 0x05, 0x48, 0xf9, 0x93, 0x4f, 0x31, 0x07, 0x9b, 0x7a, 0xd6, + 0xc2, 0x28, 0x08, 0x85, 0x31, 0xa5, 0x3a, 0xa4, 0xa3, 0x47, 0x71, 0x45, 0xa8, 0x37, 0x2f, 0xd0, 0x97, 0x23, 0xa7, + 0x25, 0x48, 0xb3, 0x94, 0xf5, 0xea, 0x47, 0xcc, 0xa6, 0xdf, 0xa0, 0x88, 0x35, 0x58, 0x64, 0xe8, 0xfb, 0xf1, 0x4b, + 0xf0, 0xfd, 0x84, 0x1a, 0x6d, 0x2b, 0xa7, 0xa1, 0xbd, 0xb2, 0x7d, 0x20, 0x69, 0xbb, 0x49, 0xd6, 0x42, 0xbe, 0xec, + 0x1c, 0x5d, 0xe7, 0xdc, 0xdc, 0x76, 0xe0, 0xda, 0xdd, 0xd9, 0xf1, 0xd4, 0x3f, 0xe3, 0xa4, 0xba, 0x59, 0x4c, 0x07, + 0xae, 0x77, 0x81, 0x2c, 0x88, 0xc6, 0xf8, 0x45, 0xbd, 0xbb, 0xb4, 0x3c, 0xa1, 0x6c, 0xc7, 0x05, 0xaa, 0xf5, 0xa0, + 0xf3, 0x08, 0xbc, 0xb5, 0x3b, 0x0f, 0x7f, 0x33, 0xfa, 0xa5, 0xa4, 0x91, 0xba, 0xb4, 0x6a, 0xdb, 0x3d, 0x94, 0x17, + 0x49, 0x74, 0x09, 0x4e, 0x23, 0xd9, 0x18, 0x27, 0x18, 0xc4, 0xed, 0xcd, 0x32, 0x99, 0x39, 0x90, 0xb3, 0x84, 0x7e, + 0x6d, 0x85, 0x5c, 0x8a, 0xed, 0x07, 0xb3, 0x0b, 0xb5, 0x1a, 0x9d, 0x46, 0x46, 0xc0, 0x9f, 0x7a, 0xf0, 0x7f, 0x7d, + 0xa6, 0x41, 0xfd, 0xf0, 0x7a, 0x07, 0x60, 0x10, 0x86, 0x4d, 0x2b, 0x17, 0x50, 0xb5, 0xa1, 0xc4, 0x48, 0x7d, 0xa8, + 0x06, 0xb2, 0xfc, 0x6d, 0x50, 0x95, 0x51, 0xc1, 0x7a, 0xf8, 0x69, 0xc3, 0x18, 0x5c, 0x53, 0x69, 0x3c, 0x4d, 0xe3, + 0xd1, 0x28, 0x61, 0x3d, 0x65, 0x1f, 0x59, 0x9d, 0x07, 0x98, 0x39, 0x60, 0x2e, 0x59, 0x7d, 0x55, 0x0c, 0xe2, 0x69, + 0x3a, 0x45, 0x27, 0x60, 0xaf, 0xe1, 0xf7, 0x09, 0x57, 0x92, 0x53, 0x1e, 0xa9, 0xb7, 0x2b, 0xe2, 0xd1, 0x73, 0x1d, + 0x97, 0x1d, 0x30, 0x16, 0x69, 0xc1, 0xdb, 0x3d, 0x9e, 0xcd, 0x82, 0xd6, 0x76, 0x1d, 0x11, 0xac, 0xd2, 0x28, 0x78, + 0x2b, 0xd0, 0xf2, 0xd0, 0x3a, 0x10, 0x5a, 0xce, 0xf2, 0x3b, 0xb2, 0x8c, 0x06, 0xc0, 0x6f, 0xe0, 0xe9, 0xa2, 0xb2, + 0x8e, 0xcc, 0xff, 0x9f, 0xdd, 0xf2, 0xd5, 0xea, 0xdd, 0xf2, 0x95, 0xda, 0x2d, 0x37, 0x73, 0xec, 0x17, 0xe3, 0x0e, + 0xfe, 0xe9, 0x55, 0x08, 0xc1, 0xaa, 0x00, 0x39, 0x2c, 0xb4, 0x8b, 0x5b, 0x5d, 0xf8, 0x8f, 0x86, 0x6e, 0x7b, 0xf8, + 0xc7, 0x07, 0x0b, 0xb0, 0x6d, 0x61, 0x21, 0xfe, 0x6b, 0xd7, 0xaa, 0x3a, 0xf7, 0xb1, 0x0e, 0x7b, 0xed, 0x2c, 0xd7, + 0x75, 0x6f, 0xde, 0xb4, 0x20, 0xaf, 0xb8, 0x13, 0x28, 0x61, 0x0c, 0xae, 0x5a, 0x74, 0x72, 0x02, 0xa5, 0xe3, 0x6c, + 0x38, 0x2f, 0xfe, 0x51, 0xc2, 0x2f, 0x89, 0x78, 0xe3, 0x96, 0x6e, 0x8c, 0xa3, 0xba, 0x8a, 0x34, 0x14, 0x35, 0xc2, + 0x42, 0xaf, 0x53, 0x50, 0x00, 0x63, 0x32, 0xa7, 0xeb, 0xdf, 0x5f, 0xb1, 0x09, 0xfe, 0x2e, 0x6b, 0xb3, 0x12, 0x99, + 0x7f, 0x2d, 0x31, 0x6e, 0x24, 0xc2, 0x67, 0xd1, 0xc0, 0x5c, 0xc3, 0xf6, 0xa3, 0xd5, 0xe0, 0x1e, 0xa9, 0x99, 0x06, + 0x4a, 0x29, 0x48, 0xbd, 0x03, 0x5e, 0x40, 0x34, 0x4f, 0xf8, 0xcd, 0xa3, 0x5e, 0xc7, 0x19, 0x4b, 0xa3, 0xde, 0x20, + 0xd0, 0xab, 0xb6, 0x77, 0x94, 0xd2, 0x5f, 0x7c, 0x75, 0x1f, 0xff, 0x88, 0xc0, 0xd7, 0x49, 0xe5, 0x1b, 0x89, 0xd8, + 0x00, 0xfa, 0x46, 0xeb, 0x35, 0xe7, 0x47, 0x68, 0x70, 0xf2, 0x7f, 0xee, 0xda, 0x1a, 0x8d, 0xf5, 0x3b, 0x35, 0x97, + 0x56, 0xe9, 0x67, 0xb5, 0xfe, 0xbc, 0xc1, 0xef, 0xd8, 0x76, 0x28, 0x1c, 0x82, 0x7a, 0x5b, 0xf9, 0x2b, 0x44, 0x96, + 0x1a, 0x2b, 0x8a, 0xdf, 0xb5, 0x7d, 0x65, 0x12, 0x53, 0x8f, 0xf5, 0xf0, 0x44, 0x3b, 0x91, 0xf2, 0xcc, 0x19, 0x7b, + 0x08, 0x3f, 0xf2, 0x27, 0x16, 0xde, 0xc3, 0xaf, 0x6f, 0x59, 0x17, 0xd3, 0x24, 0x05, 0xb3, 0x6a, 0xc2, 0xf9, 0x2c, + 0xd8, 0xda, 0x3a, 0x3f, 0x3f, 0xf7, 0xcf, 0xb7, 0xfd, 0x2c, 0x3f, 0xdd, 0xea, 0xb6, 0xdb, 0x6d, 0xfc, 0x68, 0x92, + 0x6d, 0x9d, 0xc5, 0xec, 0xfc, 0x31, 0xb8, 0x1f, 0xf6, 0x43, 0xeb, 0x91, 0xf5, 0x70, 0xdb, 0xda, 0x79, 0x60, 0x5b, + 0xa4, 0x00, 0xa0, 0x64, 0xdb, 0xb6, 0x84, 0x02, 0x08, 0x6d, 0x28, 0xde, 0xdb, 0x3d, 0x55, 0x36, 0x1c, 0x26, 0xa5, + 0x0b, 0x0b, 0x09, 0xfc, 0xb7, 0xec, 0x13, 0xab, 0x6f, 0x75, 0x51, 0xd6, 0x92, 0x6a, 0x44, 0xbd, 0xe2, 0x7e, 0x1f, + 0x46, 0xb3, 0x80, 0xd8, 0xc8, 0x2c, 0xc4, 0x30, 0x99, 0x28, 0xa5, 0x29, 0xd0, 0x2e, 0x3d, 0x81, 0x27, 0x70, 0x0b, + 0x26, 0x16, 0x3c, 0xbf, 0xea, 0x3e, 0x04, 0x1d, 0x77, 0xd6, 0xba, 0x3f, 0x6c, 0xb7, 0x3a, 0x56, 0xa7, 0xd5, 0xf5, + 0x1f, 0x5a, 0x5d, 0xf1, 0x3f, 0xc8, 0xc8, 0x6d, 0xab, 0x03, 0x4f, 0xdb, 0x16, 0xbc, 0x9f, 0xdd, 0x17, 0x29, 0x11, + 0x91, 0xbd, 0xb5, 0xb7, 0x8b, 0xbf, 0x43, 0x08, 0x90, 0xfa, 0xca, 0x16, 0xbf, 0xf5, 0xcc, 0xfe, 0xc2, 0x2c, 0xed, + 0x3c, 0x5a, 0x59, 0xdc, 0x7d, 0xb8, 0xb2, 0x78, 0xfb, 0xc1, 0xca, 0xe2, 0xfb, 0x3b, 0xf5, 0xe2, 0xad, 0x53, 0x51, + 0xa5, 0xe5, 0x42, 0x68, 0x4f, 0x23, 0x60, 0x94, 0x0b, 0xa7, 0x03, 0x70, 0xb6, 0xad, 0x16, 0xfe, 0x78, 0xd8, 0x75, + 0x75, 0xaf, 0x13, 0xec, 0xa5, 0xb1, 0x7c, 0xf8, 0x08, 0xb0, 0x7c, 0xde, 0x7d, 0x30, 0xc4, 0x76, 0x84, 0x28, 0xfc, + 0x3b, 0xdb, 0x7e, 0x34, 0x04, 0x8d, 0x60, 0xe1, 0x3f, 0xf8, 0x33, 0xd9, 0xe9, 0x0e, 0xc5, 0x4b, 0x1b, 0xeb, 0x3f, + 0x74, 0x1e, 0x16, 0xd0, 0x14, 0xff, 0xfc, 0xae, 0x4d, 0x68, 0x34, 0xe0, 0xcd, 0x71, 0xef, 0x03, 0x8d, 0x1e, 0x4d, + 0xba, 0xfe, 0x57, 0x67, 0x0f, 0xfd, 0x47, 0x93, 0xce, 0xc3, 0x0f, 0xe2, 0x2d, 0x01, 0x0a, 0x7e, 0x85, 0xff, 0x3e, + 0x6c, 0xb7, 0x27, 0xad, 0x8e, 0xff, 0xe8, 0x6c, 0xdb, 0xdf, 0x4e, 0x5a, 0x0f, 0xfc, 0x47, 0xf8, 0xaf, 0x1a, 0x6e, + 0x92, 0x4d, 0x99, 0x6d, 0xe1, 0x7a, 0x37, 0xfc, 0x5e, 0x73, 0x8e, 0xee, 0x7d, 0x6b, 0xe7, 0xfe, 0xf3, 0x47, 0xb0, + 0x46, 0x93, 0x4e, 0x17, 0xfe, 0xbf, 0xee, 0xf1, 0x03, 0x12, 0x5e, 0x0e, 0x1c, 0x31, 0x4c, 0x27, 0x55, 0x84, 0xa3, + 0x0f, 0x78, 0xdd, 0xf3, 0x7e, 0xbc, 0x2e, 0x00, 0xf2, 0xd7, 0xdb, 0x03, 0x20, 0x3f, 0xdc, 0x31, 0xc8, 0xfd, 0xd7, + 0x3f, 0x39, 0x02, 0xf2, 0x63, 0x33, 0xc8, 0xbd, 0xcf, 0x96, 0x02, 0x1d, 0x4d, 0x67, 0xed, 0x39, 0x73, 0x8e, 0x7e, + 0x62, 0x03, 0x4c, 0x9d, 0x86, 0xd6, 0x3f, 0xd4, 0xe2, 0x41, 0x19, 0x6e, 0xe4, 0x7d, 0x26, 0x76, 0x32, 0xe3, 0xd7, + 0x10, 0x84, 0xf3, 0x5b, 0x09, 0xf2, 0xe2, 0x6e, 0xf4, 0xe0, 0xfc, 0xcf, 0xa5, 0x07, 0x7d, 0xbd, 0x5f, 0xd1, 0xa3, + 0x16, 0x71, 0xa7, 0x88, 0x01, 0x39, 0xfa, 0x07, 0xf4, 0xee, 0xd8, 0x5b, 0x0c, 0xdf, 0x0a, 0x5b, 0xe4, 0x03, 0xbe, + 0xfb, 0x9c, 0xd3, 0x01, 0x91, 0x59, 0x1c, 0xda, 0x32, 0x00, 0x33, 0xc3, 0xef, 0xd3, 0xaa, 0x97, 0x33, 0x71, 0x7b, + 0x25, 0xa4, 0xab, 0x67, 0x3b, 0x3a, 0x78, 0x83, 0xc9, 0xde, 0xe1, 0x22, 0xe3, 0x11, 0xfe, 0x02, 0x24, 0x1e, 0xf3, + 0x04, 0x2f, 0xc6, 0xca, 0x4b, 0x64, 0x98, 0x9b, 0xfc, 0x1d, 0xe6, 0x55, 0xab, 0x43, 0x82, 0x29, 0x06, 0x0c, 0x5e, + 0xb1, 0x51, 0x1c, 0x39, 0xb6, 0x33, 0x83, 0x1d, 0x0b, 0x63, 0xb6, 0x6a, 0x49, 0xcd, 0x94, 0xcf, 0xec, 0xda, 0xea, + 0x77, 0xee, 0xe4, 0xf8, 0x0d, 0xb3, 0xf0, 0x48, 0x06, 0x18, 0x6d, 0xe9, 0x01, 0xc0, 0xf8, 0xaa, 0x24, 0x47, 0x61, + 0x5f, 0x59, 0x0d, 0xb6, 0x30, 0x1b, 0x3a, 0x7e, 0x17, 0xdc, 0x08, 0x2a, 0xc6, 0x6f, 0x42, 0xfd, 0xe8, 0xb4, 0xb6, + 0xc1, 0xac, 0x31, 0xba, 0xe9, 0x81, 0x06, 0x4b, 0x61, 0x24, 0x11, 0x1c, 0x68, 0x94, 0x7a, 0xfa, 0x97, 0x90, 0x55, + 0xe1, 0xa2, 0xe2, 0xf1, 0xe5, 0xa1, 0xbc, 0xf7, 0x6d, 0x63, 0xe4, 0x96, 0x22, 0xf6, 0xd5, 0x37, 0xa7, 0x36, 0x41, + 0x5d, 0xd0, 0x6f, 0x82, 0xa4, 0x73, 0x6f, 0xd4, 0x08, 0x98, 0x76, 0x6d, 0x49, 0xcf, 0x21, 0xb4, 0x85, 0x3e, 0x18, + 0xb1, 0xb3, 0x78, 0x28, 0xc5, 0xba, 0x67, 0xc9, 0xeb, 0x22, 0x2d, 0xc2, 0x22, 0xec, 0x78, 0xc2, 0x77, 0x86, 0x17, + 0xd4, 0x6a, 0x61, 0x9a, 0xd9, 0x7b, 0xf7, 0x7a, 0x1a, 0x92, 0x7a, 0xc6, 0xba, 0x8d, 0xbf, 0x96, 0xf2, 0x08, 0x7c, + 0xb5, 0x7f, 0x0c, 0xef, 0xe1, 0x2f, 0xa6, 0xbc, 0x37, 0xb0, 0x5d, 0x9f, 0x84, 0xe2, 0xbd, 0xea, 0xb7, 0x53, 0xa2, + 0x44, 0xd8, 0x04, 0xfd, 0xe5, 0xdd, 0x55, 0x91, 0x49, 0xa5, 0xd5, 0xdd, 0xa9, 0x94, 0x16, 0x3c, 0x1b, 0x52, 0x0a, + 0x04, 0x68, 0xd7, 0xdf, 0x31, 0x44, 0xe1, 0x59, 0x0b, 0x7f, 0xd6, 0x84, 0xe1, 0x7d, 0x68, 0xa0, 0xa4, 0xe1, 0x4b, + 0x68, 0xbe, 0x2d, 0x04, 0x2f, 0xf4, 0xfb, 0x89, 0x44, 0x95, 0x10, 0x53, 0x75, 0x8e, 0x59, 0x71, 0x88, 0x24, 0x72, + 0x04, 0x6c, 0xcf, 0x88, 0x37, 0x09, 0x76, 0x95, 0xd1, 0x94, 0xa7, 0xd0, 0xd7, 0xd1, 0x9f, 0x72, 0x5e, 0x55, 0xe7, + 0xd5, 0x76, 0xce, 0x8a, 0x29, 0x90, 0xe1, 0x1b, 0x07, 0x55, 0x74, 0x7d, 0x41, 0x7c, 0xd2, 0x4c, 0x6c, 0xe3, 0xea, + 0xa3, 0x6f, 0x2b, 0x32, 0x70, 0xcd, 0x4d, 0xc1, 0x2a, 0xa6, 0xa1, 0x7d, 0x81, 0x69, 0x33, 0xf8, 0xb3, 0x2a, 0x56, + 0x0f, 0x92, 0xa1, 0xfc, 0x24, 0xc2, 0xdf, 0xc8, 0x42, 0x3f, 0xca, 0x6a, 0x03, 0x72, 0xfa, 0x66, 0x25, 0x41, 0xfa, + 0x62, 0x54, 0x36, 0x91, 0x00, 0x7b, 0x01, 0x7f, 0xc1, 0x5f, 0x75, 0x5d, 0x42, 0xde, 0x83, 0xc4, 0x9c, 0x82, 0x51, + 0x9c, 0xd3, 0xf5, 0x5a, 0x85, 0x7f, 0x2d, 0xa2, 0x59, 0x91, 0x9a, 0x76, 0x25, 0x2b, 0xfa, 0x36, 0x16, 0xd9, 0x81, + 0x4c, 0x48, 0x33, 0x3f, 0xda, 0x6c, 0xde, 0x7f, 0x1c, 0x89, 0x5c, 0x34, 0xfc, 0xa8, 0xbd, 0x2d, 0x88, 0x6c, 0x83, + 0x18, 0xbb, 0x16, 0x27, 0x32, 0x6e, 0xf0, 0xda, 0x60, 0xf5, 0x5b, 0x8a, 0xcc, 0x0d, 0x6f, 0x9b, 0xab, 0xa5, 0xc7, + 0xa5, 0x75, 0x70, 0x65, 0xfc, 0xee, 0x84, 0x45, 0xdc, 0x8f, 0x52, 0xca, 0x4f, 0x72, 0x0c, 0xb1, 0xe0, 0x75, 0xd8, + 0xb6, 0x5b, 0x82, 0xe4, 0x31, 0x7e, 0x8d, 0x93, 0x20, 0xbd, 0x0f, 0x85, 0x55, 0xd2, 0xd6, 0xee, 0xa4, 0xbb, 0xf7, + 0xe6, 0x70, 0xdf, 0x12, 0xbb, 0x79, 0x77, 0x0b, 0x5e, 0x77, 0xc9, 0x1d, 0x16, 0xf9, 0x19, 0xa1, 0xc8, 0xcf, 0xb0, + 0x44, 0x52, 0x57, 0x68, 0x6f, 0x09, 0x34, 0x6d, 0x8b, 0xa5, 0x43, 0x11, 0xc3, 0x9b, 0x82, 0xbb, 0x10, 0xe3, 0x87, + 0xbd, 0xb6, 0xb0, 0x5b, 0x0b, 0x57, 0x1a, 0xb6, 0x80, 0x08, 0xf8, 0x29, 0x03, 0x81, 0xa7, 0x2a, 0xe2, 0x07, 0x6a, + 0x9d, 0xa9, 0x64, 0x17, 0x39, 0x94, 0xce, 0x4b, 0x5d, 0x6e, 0x5d, 0xcc, 0x4f, 0xa6, 0x20, 0x87, 0x54, 0x82, 0xca, + 0x7b, 0xd9, 0x61, 0x97, 0xa6, 0xa2, 0x0c, 0x94, 0xa5, 0x88, 0xe4, 0xa4, 0xb3, 0xb7, 0x1b, 0x49, 0x7b, 0x07, 0xf7, + 0x6e, 0x01, 0x9b, 0x17, 0xd4, 0x1c, 0x1a, 0x15, 0x7e, 0x9c, 0x6d, 0x9d, 0xb3, 0x93, 0x56, 0x34, 0x8b, 0xab, 0xf0, + 0x1f, 0x6a, 0xbf, 0xbd, 0x5d, 0xa5, 0x08, 0x65, 0xaa, 0xa5, 0x7c, 0x8c, 0x8c, 0x2c, 0x0e, 0x24, 0x1c, 0x31, 0x68, + 0x29, 0x63, 0x8b, 0x64, 0x34, 0x02, 0xf1, 0x01, 0x56, 0xe2, 0x5f, 0x15, 0x83, 0x94, 0x9a, 0xa0, 0xb4, 0xf7, 0xfe, + 0xf6, 0x3f, 0xfe, 0x97, 0x0c, 0x2b, 0x02, 0x59, 0x01, 0x2c, 0x4c, 0x83, 0xa9, 0x4e, 0x18, 0xd9, 0x05, 0x38, 0xa2, + 0xf1, 0xa8, 0x35, 0x89, 0x92, 0x31, 0x40, 0x50, 0x30, 0x71, 0x9d, 0x49, 0xd6, 0x03, 0x17, 0x48, 0xb0, 0xcc, 0xc3, + 0x79, 0x09, 0x5e, 0xbd, 0x08, 0x57, 0xec, 0x0f, 0xe5, 0xae, 0xaa, 0x9c, 0x61, 0x62, 0x68, 0x23, 0x93, 0xd5, 0xe0, + 0xb9, 0x5a, 0x36, 0xab, 0xfa, 0x25, 0x49, 0x52, 0x78, 0xb0, 0x5a, 0x2a, 0x2b, 0xb4, 0xd4, 0x07, 0x21, 0xff, 0xf2, + 0xcf, 0xff, 0xf1, 0xbf, 0xaa, 0x57, 0x3c, 0xdf, 0xf8, 0xdb, 0x3f, 0xfd, 0xfb, 0xff, 0xfb, 0xbf, 0xff, 0x13, 0x26, + 0x07, 0xcb, 0x33, 0x10, 0xda, 0x4a, 0x56, 0x75, 0x00, 0x22, 0xf6, 0x94, 0x55, 0x39, 0x1c, 0xf5, 0x94, 0xd7, 0x03, + 0x9a, 0x90, 0x78, 0x53, 0x42, 0x47, 0x7c, 0x4d, 0x29, 0xd2, 0x44, 0xb5, 0x1b, 0xc8, 0x07, 0x4b, 0x69, 0xd1, 0xb1, + 0xbe, 0xbd, 0xd3, 0xb6, 0xab, 0xe5, 0xdd, 0x33, 0xfa, 0x6e, 0xe1, 0xc2, 0xdc, 0x29, 0x03, 0xc7, 0xd7, 0xcb, 0xb6, + 0x50, 0x61, 0x2c, 0x2c, 0x29, 0xab, 0x72, 0x0b, 0xe3, 0xcb, 0x0b, 0x7c, 0x0d, 0xba, 0x46, 0x31, 0xad, 0x72, 0xad, + 0x4f, 0xef, 0xd7, 0x39, 0x20, 0x3a, 0xc6, 0xa5, 0x11, 0xc1, 0x32, 0x3a, 0x3b, 0x6d, 0xa1, 0x75, 0x92, 0x5c, 0x96, + 0x34, 0x8a, 0xf0, 0x66, 0xee, 0x3f, 0xfa, 0x87, 0xf2, 0x2f, 0x53, 0xb4, 0x0a, 0x2c, 0x67, 0x1a, 0x5d, 0x48, 0x1f, + 0xe7, 0x41, 0xbb, 0x3d, 0xbb, 0x70, 0x17, 0xd5, 0x0c, 0xde, 0x75, 0x93, 0x51, 0x80, 0xcd, 0x1c, 0x90, 0x0e, 0x5d, + 0x75, 0x2c, 0x0f, 0xcc, 0xfa, 0x36, 0x86, 0x7e, 0xca, 0xf2, 0xcb, 0x05, 0x85, 0x93, 0xe2, 0xdf, 0xf1, 0x70, 0x54, + 0x46, 0xde, 0xa0, 0xc4, 0xc0, 0x62, 0x61, 0xf4, 0xea, 0x8a, 0x5e, 0x93, 0xce, 0x72, 0x6e, 0x8a, 0x79, 0xb8, 0x6b, + 0x1e, 0xcb, 0xde, 0xc7, 0x83, 0xd6, 0x49, 0xc7, 0x9b, 0x74, 0x17, 0x7a, 0x78, 0xce, 0xb3, 0xa9, 0x79, 0x9a, 0xcb, + 0x22, 0x36, 0x64, 0x63, 0x15, 0xb1, 0x94, 0xf5, 0xe2, 0xa4, 0xb6, 0xfc, 0x02, 0xb7, 0x1b, 0xd0, 0x36, 0x8b, 0x78, + 0x40, 0x4c, 0xdb, 0x33, 0xcf, 0x7b, 0x23, 0x3c, 0x49, 0xcf, 0x16, 0xc6, 0x5c, 0x3d, 0xd1, 0x14, 0xe3, 0x82, 0xf5, + 0xbc, 0x9f, 0xd2, 0xa7, 0xee, 0xe6, 0x50, 0x22, 0xac, 0xf0, 0x42, 0x1e, 0xa3, 0xbe, 0xab, 0xf9, 0xe3, 0x52, 0x14, + 0x83, 0x0b, 0xbc, 0xb2, 0x5e, 0xa8, 0x45, 0x51, 0xfb, 0x02, 0xac, 0x1d, 0x02, 0xd3, 0x6e, 0xb6, 0xa2, 0x42, 0x6c, + 0xf5, 0x2e, 0x7c, 0xa1, 0x6d, 0xef, 0x68, 0x36, 0xa3, 0x86, 0x2e, 0x70, 0x23, 0xd9, 0xd0, 0x28, 0x29, 0x28, 0x45, + 0x40, 0x9c, 0xc8, 0xcb, 0x36, 0x92, 0x6d, 0xc5, 0x93, 0x3c, 0xab, 0xa7, 0xdf, 0xb9, 0xfd, 0xff, 0x00, 0x52, 0x99, + 0xa6, 0xd8, 0x89, 0x7b, 0x00, 0x00}; } // namespace web_server } // namespace esphome diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index f0a7efd12f..5c06a97d47 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -839,6 +839,7 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf return json::build_json([obj, start_config](JsonObject root) { set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config); const auto traits = obj->get_traits(); + int8_t accuracy = traits.get_temperature_accuracy_decimals(); char __buf[16]; if (start_config == DETAIL_ALL) { @@ -873,12 +874,15 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf } } + bool has_state = false; root["mode"] = PSTR_LOCAL(climate_mode_to_string(obj->mode)); - root["max_temp"] = traits.get_visual_max_temperature(); - root["min_temp"] = traits.get_visual_min_temperature(); + root["max_temp"] = value_accuracy_to_string(traits.get_visual_max_temperature(), accuracy); + root["min_temp"] = value_accuracy_to_string(traits.get_visual_min_temperature(), accuracy); root["step"] = traits.get_visual_temperature_step(); if (traits.get_supports_action()) { root["action"] = PSTR_LOCAL(climate_action_to_string(obj->action)); + root["state"] = root["action"]; + has_state = true; } if (traits.get_supports_fan_modes() && obj->fan_mode.has_value()) { root["fan_mode"] = PSTR_LOCAL(climate_fan_mode_to_string(obj->fan_mode.value())); @@ -896,14 +900,23 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf root["swing_mode"] = PSTR_LOCAL(climate_swing_mode_to_string(obj->swing_mode)); } if (traits.get_supports_current_temperature()) { - root["current_temperature"] = obj->current_temperature; + if (!std::isnan(obj->current_temperature)) { + root["current_temperature"] = value_accuracy_to_string(obj->current_temperature, accuracy); + } else { + root["current_temperature"] = "NA"; + } } if (traits.get_supports_two_point_target_temperature()) { - root["current_temperature_low"] = obj->target_temperature_low; - root["current_temperature_high"] = obj->target_temperature_low; + root["target_temperature_low"] = value_accuracy_to_string(obj->target_temperature_low, accuracy); + root["target_temperature_high"] = value_accuracy_to_string(obj->target_temperature_high, accuracy); + if (!has_state) { + root["state"] = + value_accuracy_to_string((obj->target_temperature_high + obj->target_temperature_low) / 2.0f, accuracy); + } } else { - root["target_temperature"] = obj->target_temperature; - root["state"] = obj->target_temperature; + root["target_temperature"] = value_accuracy_to_string(obj->target_temperature, accuracy); + if (!has_state) + root["state"] = root["target_temperature"]; } }); } From 479f7703a2dfebad5728758b3792bcc5839d7a60 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 11 Aug 2022 13:57:42 +1200 Subject: [PATCH 03/66] Add vector include (#3707) --- esphome/components/e131/e131.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/e131/e131.h b/esphome/components/e131/e131.h index 648cfb4585..8bf8999c21 100644 --- a/esphome/components/e131/e131.h +++ b/esphome/components/e131/e131.h @@ -4,9 +4,10 @@ #include "esphome/core/component.h" +#include #include #include -#include +#include class UDP; From aeb81e547bfd1efe720aa9267fd9a1bf55395522 Mon Sep 17 00:00:00 2001 From: Peter Galantha Date: Wed, 10 Aug 2022 19:00:09 -0700 Subject: [PATCH 04/66] Add state_class total (#3608) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/api/api.proto | 1 + esphome/components/api/api_pb2.cpp | 2 ++ esphome/components/api/api_pb2.h | 1 + esphome/components/sensor/__init__.py | 1 + esphome/components/sensor/sensor.cpp | 2 ++ esphome/components/sensor/sensor.h | 1 + esphome/const.py | 3 +++ 7 files changed, 11 insertions(+) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 3e9a62f3d8..88a74540d0 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -473,6 +473,7 @@ enum SensorStateClass { STATE_CLASS_NONE = 0; STATE_CLASS_MEASUREMENT = 1; STATE_CLASS_TOTAL_INCREASING = 2; + STATE_CLASS_TOTAL = 3; } enum SensorLastResetType { diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 70f909c07a..b91c9bd600 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -108,6 +108,8 @@ template<> const char *proto_enum_to_string(enums::Sens return "STATE_CLASS_MEASUREMENT"; case enums::STATE_CLASS_TOTAL_INCREASING: return "STATE_CLASS_TOTAL_INCREASING"; + case enums::STATE_CLASS_TOTAL: + return "STATE_CLASS_TOTAL"; default: return "UNKNOWN"; } diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index ec1cdc35ac..f9981fdbb7 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -53,6 +53,7 @@ enum SensorStateClass : uint32_t { STATE_CLASS_NONE = 0, STATE_CLASS_MEASUREMENT = 1, STATE_CLASS_TOTAL_INCREASING = 2, + STATE_CLASS_TOTAL = 3, }; enum SensorLastResetType : uint32_t { LAST_RESET_NONE = 0, diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index e88cd05c31..d6ba038057 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -109,6 +109,7 @@ STATE_CLASSES = { "": StateClasses.STATE_CLASS_NONE, "measurement": StateClasses.STATE_CLASS_MEASUREMENT, "total_increasing": StateClasses.STATE_CLASS_TOTAL_INCREASING, + "total": StateClasses.STATE_CLASS_TOTAL, } validate_state_class = cv.enum(STATE_CLASSES, lower=True, space="_") diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp index c0869631aa..a729791e7e 100644 --- a/esphome/components/sensor/sensor.cpp +++ b/esphome/components/sensor/sensor.cpp @@ -12,6 +12,8 @@ std::string state_class_to_string(StateClass state_class) { return "measurement"; case STATE_CLASS_TOTAL_INCREASING: return "total_increasing"; + case STATE_CLASS_TOTAL: + return "total"; case STATE_CLASS_NONE: default: return ""; diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index f29125ee42..ba9edd68d0 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -36,6 +36,7 @@ enum StateClass : uint8_t { STATE_CLASS_NONE = 0, STATE_CLASS_MEASUREMENT = 1, STATE_CLASS_TOTAL_INCREASING = 2, + STATE_CLASS_TOTAL = 3, }; std::string state_class_to_string(StateClass state_class); diff --git a/esphome/const.py b/esphome/const.py index e495775056..6f5eaabfcb 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -956,6 +956,9 @@ STATE_CLASS_MEASUREMENT = "measurement" # The state represents a total that only increases, a decrease is considered a reset. STATE_CLASS_TOTAL_INCREASING = "total_increasing" +# The state represents a total amount that can both increase and decrease, e.g. a net energy meter. +STATE_CLASS_TOTAL = "total" + KEY_CORE = "core" KEY_TARGET_PLATFORM = "target_platform" KEY_TARGET_FRAMEWORK = "target_framework" From 1cf213dad8f4b0e1e00d8bc25f5b4d2351794c15 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Sun, 14 Aug 2022 22:21:08 +0200 Subject: [PATCH 05/66] add gradient color V2.0 (#3709) --- esphome/core/color.h | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/esphome/core/color.h b/esphome/core/color.h index 4e7733ca85..7596eeb0cf 100644 --- a/esphome/core/color.h +++ b/esphome/core/color.h @@ -139,9 +139,19 @@ struct Color { return Color(uint8_t((uint16_t(r) * 255U / max_rgb)), uint8_t((uint16_t(g) * 255U / max_rgb)), uint8_t((uint16_t(b) * 255U / max_rgb)), w); } - Color fade_to_white(uint8_t amnt) { return Color(255, 255, 255, 255) - (*this * amnt); } - Color fade_to_black(uint8_t amnt) { return *this * amnt; } - Color gradient(const Color &to_color, uint8_t amnt) { return (*this * amnt) + (to_color * (255 - amnt)); } + + Color gradient(const Color &to_color, uint8_t amnt) { + Color new_color; + float amnt_f = float(amnt) / 255.0f; + new_color.r = amnt_f * (to_color.r - (*this).r) + (*this).r; + new_color.g = amnt_f * (to_color.g - (*this).g) + (*this).g; + new_color.b = amnt_f * (to_color.b - (*this).b) + (*this).b; + new_color.w = amnt_f * (to_color.w - (*this).w) + (*this).w; + return new_color; + } + Color fade_to_white(uint8_t amnt) { return (*this).gradient(Color::WHITE, amnt); } + Color fade_to_black(uint8_t amnt) { return (*this).gradient(Color::BLACK, amnt); } + Color lighten(uint8_t delta) { return *this + delta; } Color darken(uint8_t delta) { return *this - delta; } From e008b054cbf76d0271cba85f0e0574c7c3a07118 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sun, 14 Aug 2022 15:16:56 -0700 Subject: [PATCH 06/66] support modifying the apds9960 settings (#3708) Co-authored-by: Samuel Sieb Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/apds9960/__init__.py | 41 ++++++++++++++++++++++++ esphome/components/apds9960/apds9960.cpp | 23 +++++++------ esphome/components/apds9960/apds9960.h | 14 ++++++++ 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/esphome/components/apds9960/__init__.py b/esphome/components/apds9960/__init__.py index 8de83251b7..37dc4c0b28 100644 --- a/esphome/components/apds9960/__init__.py +++ b/esphome/components/apds9960/__init__.py @@ -8,6 +8,27 @@ AUTO_LOAD = ["sensor", "binary_sensor"] MULTI_CONF = True CONF_APDS9960_ID = "apds9960_id" +CONF_LED_DRIVE = "led_drive" +CONF_PROXIMITY_GAIN = "proximity_gain" +CONF_AMBIENT_LIGHT_GAIN = "ambient_light_gain" +CONF_GESTURE_LED_DRIVE = "gesture_led_drive" +CONF_GESTURE_GAIN = "gesture_gain" +CONF_GESTURE_WAIT_TIME = "gesture_wait_time" + +DRIVE_LEVELS = {"100ma": 0, "50ma": 1, "25ma": 2, "12.5ma": 3} +PROXIMITY_LEVELS = {"1x": 0, "2x": 1, "4x": 2, "8x": 3} +AMBIENT_LEVELS = {"1x": 0, "4x": 1, "16x": 2, "64x": 3} +GESTURE_LEVELS = {"1x": 0, "2x": 1, "4x": 2, "8x": 3} +GESTURE_WAIT_TIMES = { + "0ms": 0, + "2.8ms": 1, + "5.6ms": 2, + "8.4ms": 3, + "14ms": 4, + "22.4ms": 5, + "30.8ms": 6, + "39.2ms": 7, +} apds9960_nds = cg.esphome_ns.namespace("apds9960") APDS9960 = apds9960_nds.class_("APDS9960", cg.PollingComponent, i2c.I2CDevice) @@ -16,6 +37,20 @@ CONFIG_SCHEMA = ( cv.Schema( { cv.GenerateID(): cv.declare_id(APDS9960), + cv.Optional(CONF_LED_DRIVE, "100mA"): cv.enum(DRIVE_LEVELS, lower=True), + cv.Optional(CONF_PROXIMITY_GAIN, "4x"): cv.enum( + PROXIMITY_LEVELS, lower=True + ), + cv.Optional(CONF_AMBIENT_LIGHT_GAIN, "4x"): cv.enum( + AMBIENT_LEVELS, lower=True + ), + cv.Optional(CONF_GESTURE_LED_DRIVE, "100mA"): cv.enum( + DRIVE_LEVELS, lower=True + ), + cv.Optional(CONF_GESTURE_GAIN, "4x"): cv.enum(GESTURE_LEVELS, lower=True), + cv.Optional(CONF_GESTURE_WAIT_TIME, "2.8ms"): cv.enum( + GESTURE_WAIT_TIMES, lower=True + ), } ) .extend(cv.polling_component_schema("60s")) @@ -27,3 +62,9 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) await i2c.register_i2c_device(var, config) + cg.add(var.set_led_drive(config[CONF_LED_DRIVE])) + cg.add(var.set_proximity_gain(config[CONF_PROXIMITY_GAIN])) + cg.add(var.set_ambient_gain(config[CONF_AMBIENT_LIGHT_GAIN])) + cg.add(var.set_gesture_led_drive(config[CONF_GESTURE_LED_DRIVE])) + cg.add(var.set_gesture_gain(config[CONF_GESTURE_GAIN])) + cg.add(var.set_gesture_wait_time(config[CONF_GESTURE_WAIT_TIME])) diff --git a/esphome/components/apds9960/apds9960.cpp b/esphome/components/apds9960/apds9960.cpp index 5ba3afbacc..05091f3f7d 100644 --- a/esphome/components/apds9960/apds9960.cpp +++ b/esphome/components/apds9960/apds9960.cpp @@ -46,16 +46,16 @@ void APDS9960::setup() { uint8_t val = 0; APDS9960_ERROR_CHECK(this->read_byte(0x8F, &val)); val &= 0b00111111; - uint8_t led_drive = 0; // led drive, 0 -> 100mA, 1 -> 50mA, 2 -> 25mA, 3 -> 12.5mA - val |= (led_drive & 0b11) << 6; + // led drive, 0 -> 100mA, 1 -> 50mA, 2 -> 25mA, 3 -> 12.5mA + val |= (this->led_drive_ & 0b11) << 6; val &= 0b11110011; - uint8_t proximity_gain = 2; // proximity gain, 0 -> 1x, 1 -> 2X, 2 -> 4X, 4 -> 8X - val |= (proximity_gain & 0b11) << 2; + // proximity gain, 0 -> 1x, 1 -> 2X, 2 -> 4X, 3 -> 8X + val |= (this->proximity_gain_ & 0b11) << 2; val &= 0b11111100; - uint8_t ambient_gain = 1; // ambient light gain, 0 -> 1x, 1 -> 4x, 2 -> 16x, 3 -> 64x - val |= (ambient_gain & 0b11) << 0; + // ambient light gain, 0 -> 1x, 1 -> 4x, 2 -> 16x, 3 -> 64x + val |= (this->ambient_gain_ & 0b11) << 0; APDS9960_WRITE_BYTE(0x8F, val); // Pers (0x8C) -> 0x11 (2 consecutive proximity or ALS for interrupt) @@ -75,19 +75,18 @@ void APDS9960::setup() { // GConf 2 (0xA3, gesture config 2) -> APDS9960_ERROR_CHECK(this->read_byte(0xA3, &val)); val &= 0b10011111; - uint8_t gesture_gain = 2; // gesture gain, 0 -> 1x, 1 -> 2x, 2 -> 4x, 3 -> 8x - val |= (gesture_gain & 0b11) << 5; + // gesture gain, 0 -> 1x, 1 -> 2x, 2 -> 4x, 3 -> 8x + val |= (this->gesture_gain_ & 0b11) << 5; val &= 0b11100111; - uint8_t gesture_led_drive = 0; // gesture led drive, 0 -> 100mA, 1 -> 50mA, 2 -> 25mA, 3 -> 12.5mA - val |= (gesture_led_drive & 0b11) << 3; + // gesture led drive, 0 -> 100mA, 1 -> 50mA, 2 -> 25mA, 3 -> 12.5mA + val |= (this->gesture_led_drive_ & 0b11) << 3; val &= 0b11111000; // gesture wait time // 0 -> 0ms, 1 -> 2.8ms, 2 -> 5.6ms, 3 -> 8.4ms // 4 -> 14.0ms, 5 -> 22.4 ms, 6 -> 30.8ms, 7 -> 39.2 ms - uint8_t gesture_wait_time = 1; // gesture wait time - val |= (gesture_wait_time & 0b111) << 0; + val |= (this->gesture_wait_time_ & 0b111) << 0; APDS9960_WRITE_BYTE(0xA3, val); // GOffsetU (0xA4) -> 0x00 (no offset) diff --git a/esphome/components/apds9960/apds9960.h b/esphome/components/apds9960/apds9960.h index ae44b5da0f..23d9835640 100644 --- a/esphome/components/apds9960/apds9960.h +++ b/esphome/components/apds9960/apds9960.h @@ -16,6 +16,13 @@ class APDS9960 : public PollingComponent, public i2c::I2CDevice { void update() override; void loop() override; + void set_led_drive(uint8_t level) { this->led_drive_ = level; } + void set_proximity_gain(uint8_t gain) { this->proximity_gain_ = gain; } + void set_ambient_gain(uint8_t gain) { this->ambient_gain_ = gain; } + void set_gesture_led_drive(uint8_t level) { this->gesture_led_drive_ = level; } + void set_gesture_gain(uint8_t gain) { this->gesture_gain_ = gain; } + void set_gesture_wait_time(uint8_t wait_time) { this->gesture_wait_time_ = wait_time; } + void set_red_channel(sensor::Sensor *red_channel) { red_channel_ = red_channel; } void set_green_channel(sensor::Sensor *green_channel) { green_channel_ = green_channel; } void set_blue_channel(sensor::Sensor *blue_channel) { blue_channel_ = blue_channel; } @@ -36,6 +43,13 @@ class APDS9960 : public PollingComponent, public i2c::I2CDevice { void report_gesture_(int gesture); void process_dataset_(int up, int down, int left, int right); + uint8_t led_drive_; + uint8_t proximity_gain_; + uint8_t ambient_gain_; + uint8_t gesture_led_drive_; + uint8_t gesture_gain_; + uint8_t gesture_wait_time_; + sensor::Sensor *red_channel_{nullptr}; sensor::Sensor *green_channel_{nullptr}; sensor::Sensor *blue_channel_{nullptr}; From 01a4443b6c45e4d4e95a43c8410a3469dfc2e630 Mon Sep 17 00:00:00 2001 From: anatoly-savchenkov <48646998+anatoly-savchenkov@users.noreply.github.com> Date: Mon, 15 Aug 2022 01:28:29 +0300 Subject: [PATCH 07/66] Webui small fixes (#3713) --- esphome/components/climate/climate_traits.cpp | 11 +- esphome/components/web_server/server_index.h | 1148 ++++++++--------- esphome/components/web_server/web_server.cpp | 18 +- esphome/core/helpers.cpp | 13 + esphome/core/helpers.h | 3 + 5 files changed, 604 insertions(+), 589 deletions(-) diff --git a/esphome/components/climate/climate_traits.cpp b/esphome/components/climate/climate_traits.cpp index 16c9cd05be..38ded6cdf7 100644 --- a/esphome/components/climate/climate_traits.cpp +++ b/esphome/components/climate/climate_traits.cpp @@ -1,19 +1,10 @@ #include "climate_traits.h" -#include namespace esphome { namespace climate { int8_t ClimateTraits::get_temperature_accuracy_decimals() const { - // use printf %g to find number of digits based on temperature step - char buf[32]; - sprintf(buf, "%.5g", this->visual_temperature_step_); - std::string str{buf}; - size_t dot_pos = str.find('.'); - if (dot_pos == std::string::npos) - return 0; - - return str.length() - dot_pos - 1; + return step_to_accuracy_decimals(this->visual_temperature_step_); } } // namespace climate diff --git a/esphome/components/web_server/server_index.h b/esphome/components/web_server/server_index.h index 39c170f15b..75c7130151 100644 --- a/esphome/components/web_server/server_index.h +++ b/esphome/components/web_server/server_index.h @@ -6,580 +6,580 @@ namespace esphome { namespace web_server { const uint8_t INDEX_GZ[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xbd, 0x7d, 0xc9, 0x76, 0xdb, 0xc8, 0xb2, 0xe0, 0xba, - 0xcf, 0xe9, 0x3f, 0xe8, 0x0d, 0x84, 0xd2, 0x93, 0x81, 0x12, 0x08, 0x91, 0x94, 0x65, 0xbb, 0x40, 0x41, 0xbc, 0xf2, - 0x50, 0xd7, 0xae, 0xf2, 0x54, 0x96, 0xec, 0x1a, 0x54, 0x2c, 0x0b, 0x22, 0x93, 0x22, 0xca, 0x20, 0xc0, 0x02, 0x92, - 0x1a, 0x8a, 0xc2, 0x3b, 0xbd, 0xea, 0x55, 0x9f, 0xd3, 0xe3, 0xe2, 0x6d, 0xfa, 0xbc, 0x5e, 0xf4, 0x47, 0xf4, 0xba, - 0x3f, 0xe5, 0xfe, 0x40, 0xbf, 0x4f, 0xe8, 0x88, 0xc8, 0x01, 0x09, 0x90, 0x1a, 0x5c, 0xaf, 0xfa, 0x1e, 0x0f, 0x02, - 0x72, 0x8c, 0x88, 0x8c, 0x8c, 0x29, 0x23, 0xa1, 0xdd, 0xb5, 0x51, 0x36, 0xe4, 0x97, 0x33, 0x66, 0x4d, 0xf8, 0x34, - 0xd9, 0xdb, 0x95, 0xff, 0xb3, 0x68, 0xb4, 0xb7, 0x9b, 0xc4, 0xe9, 0x27, 0x2b, 0x67, 0x49, 0x18, 0x0f, 0xb3, 0xd4, - 0x9a, 0xe4, 0x6c, 0x1c, 0x8e, 0x22, 0x1e, 0x05, 0xf1, 0x34, 0x3a, 0x65, 0xd6, 0xd6, 0xde, 0xee, 0x94, 0xf1, 0xc8, - 0x1a, 0x4e, 0xa2, 0xbc, 0x60, 0x3c, 0x7c, 0x7f, 0xf8, 0x75, 0xeb, 0xd1, 0xde, 0x6e, 0x31, 0xcc, 0xe3, 0x19, 0xb7, - 0x70, 0xc8, 0x70, 0x9a, 0x8d, 0xe6, 0x09, 0xdb, 0x3b, 0x8b, 0x72, 0xeb, 0x82, 0x85, 0x6f, 0x4e, 0x7e, 0x65, 0x43, - 0xee, 0x8f, 0xd8, 0x38, 0x4e, 0xd9, 0xdb, 0x3c, 0x9b, 0xb1, 0x9c, 0x5f, 0x7a, 0xcf, 0x56, 0x57, 0xc4, 0xac, 0xf0, - 0x0e, 0x74, 0xd5, 0x29, 0xe3, 0x6f, 0xce, 0x53, 0xd5, 0xe7, 0x29, 0x13, 0x93, 0x64, 0x79, 0xe1, 0xf1, 0x6b, 0xda, - 0x1c, 0x5c, 0x4e, 0x4f, 0xb2, 0xa4, 0xf0, 0x3e, 0xe9, 0xfa, 0x59, 0x9e, 0xf1, 0x0c, 0xc1, 0xf2, 0x27, 0x51, 0x61, - 0xb4, 0xf4, 0x9e, 0xac, 0x68, 0x32, 0x93, 0x95, 0x2f, 0x8a, 0x67, 0xe9, 0x7c, 0xca, 0xf2, 0xe8, 0x24, 0x61, 0x5e, - 0xc1, 0x42, 0x87, 0x79, 0xdc, 0x8b, 0xdd, 0x70, 0x8f, 0x5b, 0x71, 0x6a, 0xb1, 0xfe, 0x05, 0xa3, 0x92, 0x05, 0xd3, - 0xad, 0x82, 0xb5, 0xb6, 0x07, 0xe4, 0x1a, 0xc7, 0xa7, 0x73, 0xfd, 0x7e, 0x9e, 0xc7, 0x5c, 0x3d, 0x9f, 0x45, 0xc9, - 0x9c, 0x05, 0x71, 0xe9, 0x06, 0xec, 0x88, 0x0f, 0xc2, 0xd8, 0x7b, 0x42, 0x83, 0xc2, 0x90, 0x8b, 0x71, 0x96, 0x3b, - 0x48, 0xab, 0x18, 0xc7, 0xe6, 0x57, 0x57, 0x0e, 0x0f, 0x17, 0xa5, 0xeb, 0x7e, 0x62, 0xfe, 0x30, 0x4a, 0x12, 0x07, - 0x27, 0xde, 0xd8, 0x28, 0x70, 0xc6, 0xd8, 0xe3, 0x47, 0xf1, 0xc0, 0xed, 0xc5, 0x63, 0x87, 0x33, 0xb7, 0xea, 0x97, - 0x8d, 0x2d, 0xce, 0x1c, 0xee, 0xba, 0x4f, 0xae, 0xef, 0x93, 0x33, 0x3e, 0xcf, 0x01, 0xf6, 0xd2, 0x7b, 0xa3, 0x66, - 0x7e, 0x86, 0xf5, 0x07, 0xd4, 0xb1, 0x07, 0xb0, 0x17, 0xdc, 0xfa, 0x10, 0x9e, 0xc7, 0xe9, 0x28, 0x3b, 0xf7, 0x0f, - 0x26, 0x11, 0xfc, 0x78, 0x97, 0x65, 0x7c, 0x63, 0xc3, 0x39, 0xcb, 0xe2, 0x91, 0xd5, 0x0e, 0x43, 0xb3, 0xf2, 0xf2, - 0xc9, 0xc1, 0xc1, 0xd5, 0x55, 0xa3, 0xc0, 0x4f, 0x23, 0x1e, 0x9f, 0x31, 0xd1, 0x19, 0x00, 0xb0, 0xe1, 0xe7, 0x8c, - 0xb3, 0xd1, 0x01, 0xbf, 0x4c, 0xa0, 0x94, 0x31, 0x5e, 0xd8, 0x80, 0xe3, 0xd3, 0x6c, 0x08, 0x64, 0x4b, 0x0d, 0xc2, - 0x43, 0xd3, 0x9c, 0xcd, 0x92, 0x68, 0xc8, 0xb0, 0x1e, 0x46, 0xaa, 0x7a, 0x54, 0x8d, 0xbc, 0xaf, 0x43, 0xb1, 0xbc, - 0x8e, 0xeb, 0xc5, 0x2c, 0x4c, 0xd9, 0xb9, 0xf5, 0x2a, 0x9a, 0xf5, 0x86, 0x49, 0x54, 0x14, 0xc0, 0xaf, 0x0b, 0x42, - 0x21, 0x9f, 0x0f, 0x81, 0x41, 0x08, 0xc1, 0x05, 0x92, 0x69, 0x12, 0x17, 0xfe, 0xc7, 0xf5, 0x61, 0x51, 0xbc, 0x63, - 0xc5, 0x3c, 0xe1, 0xeb, 0x21, 0xac, 0x05, 0x5f, 0x0b, 0xc3, 0xaf, 0x5d, 0x3e, 0xc9, 0xb3, 0x73, 0xeb, 0x59, 0x9e, - 0x43, 0x73, 0x1b, 0xa6, 0x14, 0x0d, 0xac, 0xb8, 0xb0, 0xd2, 0x8c, 0x5b, 0x7a, 0x30, 0x5c, 0x40, 0xdf, 0x7a, 0x5f, - 0x30, 0xeb, 0x78, 0x9e, 0x16, 0xd1, 0x98, 0x41, 0xd3, 0x63, 0x2b, 0xcb, 0xad, 0x63, 0x18, 0xf4, 0x18, 0x96, 0xac, - 0xe0, 0xb0, 0x6b, 0x7c, 0xdb, 0xed, 0xd1, 0x5c, 0x50, 0x78, 0xc8, 0x2e, 0x78, 0xc8, 0x4a, 0x60, 0x4c, 0xab, 0xd0, - 0x68, 0x38, 0xee, 0x22, 0x81, 0x02, 0x16, 0xc6, 0x0c, 0x59, 0xd6, 0x31, 0x1b, 0xeb, 0xc5, 0xf9, 0xb0, 0xb1, 0xa1, - 0x69, 0x0d, 0x34, 0x71, 0xa0, 0x6d, 0xd1, 0x68, 0xeb, 0x09, 0xc4, 0x6b, 0x24, 0x72, 0x3d, 0xe6, 0x4b, 0xf2, 0x1d, - 0x5c, 0xa6, 0xc3, 0xfa, 0xd8, 0x50, 0x59, 0xf2, 0xec, 0x80, 0xe7, 0x71, 0x7a, 0x0a, 0x40, 0xc8, 0x99, 0xcc, 0x26, - 0x65, 0x29, 0x16, 0xff, 0x0d, 0x0b, 0x59, 0xb8, 0x87, 0xa3, 0xe7, 0xcc, 0xb1, 0x0b, 0xea, 0x61, 0x87, 0x21, 0x92, - 0x1e, 0x18, 0x8c, 0xf5, 0x59, 0xc0, 0x36, 0x6d, 0xdb, 0xfb, 0xda, 0xf5, 0x2e, 0x91, 0x83, 0x7c, 0xdf, 0x27, 0xf6, - 0x15, 0x9d, 0xe3, 0xb0, 0x83, 0x40, 0xfb, 0x09, 0x4b, 0x4f, 0xf9, 0xa4, 0xcf, 0x8e, 0xda, 0x83, 0x80, 0x03, 0x54, - 0xa3, 0xf9, 0x90, 0x39, 0xc8, 0x8f, 0x5e, 0x8e, 0xdb, 0x67, 0xd3, 0x81, 0x29, 0x70, 0x61, 0xd6, 0x08, 0xc7, 0xda, - 0xd2, 0xb8, 0x8a, 0x45, 0x15, 0x60, 0xc8, 0xe7, 0x36, 0xec, 0xb0, 0x13, 0x96, 0x1b, 0x70, 0xe8, 0x66, 0xbd, 0xda, - 0x0a, 0xce, 0x61, 0x85, 0xa0, 0x9f, 0x35, 0x9e, 0xa7, 0x43, 0x1e, 0x83, 0xe0, 0xb2, 0x37, 0x01, 0x5c, 0xb1, 0x72, - 0x7a, 0xe1, 0x6c, 0xb7, 0x74, 0x9d, 0xd8, 0xdd, 0x64, 0x47, 0xf9, 0x66, 0x67, 0xe0, 0x21, 0x94, 0x9a, 0xf8, 0x12, - 0xf1, 0x18, 0x10, 0x2c, 0xbd, 0xb7, 0x4c, 0x6f, 0xcf, 0x0f, 0x7d, 0xe6, 0x2f, 0xf3, 0x71, 0xc8, 0xfd, 0x69, 0x34, - 0x43, 0x6c, 0x18, 0xf1, 0x40, 0x94, 0x0e, 0x11, 0xba, 0xda, 0xba, 0x20, 0xc5, 0xfc, 0x8a, 0x05, 0x5c, 0x20, 0x08, - 0xec, 0xd9, 0x67, 0xd1, 0x70, 0x02, 0x5b, 0xbc, 0x22, 0xdc, 0x48, 0x6d, 0x87, 0x61, 0xce, 0x22, 0xce, 0x9e, 0x25, - 0x0c, 0xdf, 0x70, 0x05, 0xa0, 0xa7, 0xed, 0x7a, 0xb9, 0xda, 0x77, 0x49, 0xcc, 0x5f, 0x67, 0x30, 0x4f, 0x4f, 0x30, - 0x09, 0x70, 0x71, 0xbe, 0xb1, 0x11, 0x23, 0x8b, 0xec, 0x73, 0x58, 0xad, 0x93, 0x39, 0x08, 0x01, 0x3b, 0xc5, 0x16, - 0x36, 0x50, 0xdb, 0x8b, 0x7d, 0x0e, 0x44, 0x7c, 0x92, 0xa5, 0x1c, 0x86, 0x03, 0x78, 0x35, 0x07, 0xf9, 0xd1, 0x6c, - 0xc6, 0xd2, 0xd1, 0x93, 0x49, 0x9c, 0x8c, 0x80, 0x1a, 0x25, 0xe0, 0x9b, 0xb1, 0x10, 0xf0, 0x04, 0x64, 0x82, 0x9b, - 0x31, 0xa2, 0xe5, 0x43, 0x46, 0xe6, 0xa1, 0x6d, 0xf7, 0x50, 0x02, 0x49, 0x2c, 0x50, 0x06, 0xd1, 0xc2, 0xbd, 0x03, - 0xd1, 0x5f, 0xb8, 0x7c, 0x33, 0x8c, 0xf5, 0x32, 0x4a, 0x02, 0xbf, 0x41, 0x49, 0x03, 0xf4, 0x67, 0x20, 0x03, 0x7b, - 0x28, 0xb8, 0xbe, 0x97, 0x52, 0x27, 0x65, 0x0a, 0x43, 0x20, 0xc0, 0x10, 0x25, 0x88, 0xa4, 0xc1, 0xdb, 0x2c, 0xb9, - 0x1c, 0xc7, 0x49, 0x72, 0x30, 0x9f, 0xcd, 0xb2, 0x9c, 0x7b, 0xdf, 0x84, 0x0b, 0x9e, 0x55, 0xb8, 0xd2, 0x26, 0x2f, - 0xce, 0x63, 0x8e, 0x04, 0x75, 0x17, 0xc3, 0x08, 0x96, 0xfa, 0x71, 0x96, 0x25, 0x2c, 0x4a, 0x01, 0x0d, 0xd6, 0xb7, - 0xed, 0x20, 0x9d, 0x27, 0x49, 0xef, 0x04, 0x86, 0xfd, 0xd4, 0xa3, 0x6a, 0x21, 0xf1, 0x03, 0x7a, 0xde, 0xcf, 0xf3, - 0xe8, 0x12, 0x1a, 0x62, 0x1b, 0xe0, 0x45, 0x58, 0xad, 0x6f, 0x0e, 0xde, 0xbc, 0xf6, 0x05, 0xe3, 0xc7, 0xe3, 0x4b, - 0x00, 0xb4, 0xac, 0xa4, 0xe6, 0x38, 0xcf, 0xa6, 0x8d, 0xa9, 0x91, 0x0e, 0x71, 0xc8, 0x7a, 0xd7, 0x80, 0x10, 0xd3, - 0xc8, 0xb0, 0x4a, 0xcc, 0x84, 0xe0, 0x35, 0xf1, 0xb3, 0xac, 0xc4, 0x3d, 0xd0, 0xc7, 0x87, 0x40, 0x14, 0xc3, 0x94, - 0x37, 0x43, 0xcb, 0xf3, 0xcb, 0x45, 0x1c, 0x12, 0x9c, 0x33, 0xd4, 0xbf, 0x08, 0xe3, 0x30, 0x82, 0xd9, 0x17, 0x62, - 0xc0, 0x52, 0x41, 0x1c, 0x97, 0xa5, 0x97, 0x68, 0x26, 0x46, 0x89, 0x87, 0x02, 0x85, 0xc3, 0x36, 0xba, 0xba, 0x62, - 0xf0, 0xe2, 0x7a, 0xdf, 0x86, 0x8b, 0x48, 0xe1, 0x83, 0x1a, 0x0a, 0xf7, 0x57, 0x20, 0xe4, 0x04, 0x6a, 0xb2, 0x33, - 0xd0, 0x83, 0x00, 0xe7, 0x37, 0x1e, 0xe8, 0xff, 0x04, 0xa1, 0x58, 0xeb, 0x78, 0xa0, 0x41, 0x9f, 0x4c, 0xa2, 0xf4, - 0x94, 0x8d, 0x82, 0x84, 0x95, 0x52, 0xf2, 0xee, 0x5b, 0xb0, 0xc6, 0xc0, 0x4e, 0x85, 0xf5, 0xfc, 0xf0, 0xd5, 0x4b, - 0xb9, 0x72, 0x35, 0x61, 0x0c, 0x8b, 0x34, 0x07, 0xb5, 0x0a, 0x62, 0x5b, 0x8a, 0xe3, 0x67, 0x5c, 0x49, 0x6f, 0x51, - 0x12, 0x17, 0xef, 0x67, 0x60, 0x62, 0xb0, 0xb7, 0x30, 0x0c, 0x4c, 0x1f, 0xc2, 0x54, 0x54, 0x0e, 0xf3, 0x89, 0x8a, - 0x91, 0x2e, 0x82, 0xce, 0x02, 0x53, 0xf1, 0x9a, 0x39, 0x6e, 0x09, 0xac, 0xca, 0xe3, 0xa1, 0x15, 0x8d, 0x46, 0x2f, - 0xd2, 0x98, 0xc7, 0x51, 0x12, 0xff, 0x4e, 0x94, 0x5c, 0x20, 0x8f, 0xf1, 0x9e, 0x5c, 0x04, 0xc0, 0x9d, 0x7a, 0x24, - 0xae, 0x12, 0xb2, 0x6b, 0x44, 0x0c, 0x21, 0x2d, 0x93, 0xf0, 0x68, 0x20, 0xc1, 0x4b, 0xfc, 0xd9, 0xbc, 0x98, 0x20, - 0x61, 0xe5, 0xc0, 0x28, 0xc8, 0xb3, 0x93, 0x82, 0xe5, 0x67, 0x6c, 0xa4, 0x39, 0xa0, 0x00, 0xac, 0xa8, 0x39, 0x18, - 0x2f, 0x34, 0xa3, 0xa3, 0x74, 0x28, 0x83, 0xa1, 0x7a, 0xa6, 0x98, 0x65, 0x92, 0x99, 0xb5, 0x85, 0xa3, 0xa5, 0x80, - 0x23, 0x8c, 0x0a, 0x29, 0x09, 0xf2, 0x50, 0x61, 0x38, 0x01, 0x29, 0x04, 0x5a, 0xc1, 0xdc, 0xe6, 0x4a, 0x93, 0x3d, - 0x9b, 0x93, 0x4a, 0xc8, 0xa1, 0x23, 0x6c, 0x64, 0x82, 0x34, 0x77, 0x61, 0x57, 0x81, 0x94, 0x97, 0xe0, 0x0a, 0x29, - 0xa2, 0xcc, 0x1c, 0x64, 0x80, 0xf0, 0x5b, 0xa1, 0x0b, 0x7d, 0x6c, 0x41, 0x6c, 0xe0, 0xeb, 0x95, 0x07, 0xc2, 0x4a, - 0xbc, 0x2b, 0x44, 0xbc, 0x6b, 0xc0, 0xc6, 0x89, 0x91, 0x9f, 0xbc, 0x35, 0xee, 0xa7, 0xd9, 0xfe, 0x70, 0xc8, 0x8a, - 0x22, 0x03, 0xd8, 0xd6, 0xa8, 0xfd, 0x75, 0x86, 0x16, 0x50, 0xd2, 0xd5, 0xb2, 0xce, 0x2e, 0x48, 0x83, 0x9b, 0x6a, - 0x45, 0xe9, 0xf4, 0xc0, 0xfe, 0xf8, 0x11, 0x64, 0xb6, 0x27, 0xc9, 0x00, 0x54, 0x5f, 0x36, 0xfc, 0x84, 0x3d, 0x53, - 0xa7, 0xcc, 0x4a, 0xfb, 0xd2, 0xa9, 0x83, 0xe4, 0xc1, 0xb0, 0x6e, 0x69, 0x2c, 0xe8, 0xca, 0xa1, 0x71, 0x35, 0xa4, - 0x82, 0x5c, 0x9c, 0x92, 0xca, 0x36, 0x96, 0x11, 0xac, 0xb6, 0xd2, 0x23, 0xd2, 0x2b, 0x6c, 0x0a, 0x02, 0xf4, 0x88, - 0x0d, 0x7a, 0xb2, 0x3e, 0xcc, 0x05, 0xe5, 0x72, 0xf6, 0xdb, 0x9c, 0x15, 0x5c, 0xb0, 0x2e, 0x8c, 0x5b, 0xc0, 0xb8, - 0xe5, 0x92, 0x75, 0x58, 0xb3, 0x1d, 0x57, 0xc1, 0xf6, 0x66, 0x86, 0x7a, 0xac, 0x40, 0x4e, 0xbe, 0x99, 0x9d, 0x10, - 0x56, 0xe6, 0x5e, 0x5d, 0x7d, 0xab, 0x06, 0xa9, 0x96, 0x52, 0xdb, 0x40, 0x8d, 0x35, 0xb1, 0x55, 0x93, 0x91, 0xed, - 0x4a, 0x85, 0xba, 0xd6, 0xe9, 0xd5, 0xf8, 0x00, 0xf6, 0x5c, 0x5b, 0xb3, 0x74, 0x65, 0x6c, 0xbf, 0x55, 0x34, 0x7d, - 0x23, 0x46, 0x26, 0x6b, 0x94, 0xdd, 0xce, 0x3d, 0x6a, 0xc7, 0x43, 0xdb, 0xa5, 0xba, 0x4a, 0x30, 0xcc, 0xeb, 0x82, - 0xa1, 0x09, 0xf5, 0x4c, 0x77, 0xb1, 0x35, 0x53, 0xb1, 0x50, 0xad, 0xb5, 0x72, 0x20, 0x78, 0x78, 0x04, 0xc6, 0xc9, - 0x4a, 0xff, 0xe0, 0x75, 0x34, 0x65, 0x48, 0x51, 0xef, 0xba, 0x06, 0xd2, 0x81, 0x80, 0x26, 0x83, 0xa6, 0x7a, 0xe3, - 0xae, 0xb0, 0x9a, 0xea, 0xfb, 0x2b, 0x06, 0x2b, 0x02, 0xec, 0xeb, 0x72, 0xc5, 0x12, 0x91, 0xde, 0x14, 0x5c, 0xa2, - 0xe9, 0x23, 0xca, 0xc4, 0x9a, 0x90, 0x82, 0x07, 0xe4, 0x61, 0xf9, 0x1b, 0x0b, 0x27, 0x5b, 0x31, 0x85, 0x23, 0x47, - 0x99, 0x02, 0x74, 0x26, 0x25, 0x00, 0xe2, 0x92, 0x7e, 0xd6, 0x36, 0x16, 0x92, 0xed, 0x00, 0xf9, 0xc0, 0x1f, 0x27, - 0x11, 0x77, 0x3a, 0x5b, 0x6d, 0x17, 0xf8, 0x10, 0x84, 0x38, 0xe8, 0x08, 0x30, 0xef, 0x2b, 0x54, 0x18, 0xa2, 0x12, - 0xbb, 0xdc, 0x07, 0xa3, 0x68, 0x12, 0x8f, 0xb9, 0x93, 0xa1, 0x12, 0x71, 0x4b, 0x96, 0x80, 0x92, 0xd1, 0xfb, 0x0a, - 0xa4, 0x04, 0x17, 0xd2, 0x45, 0x54, 0x6b, 0x81, 0xa6, 0x20, 0x25, 0x29, 0x45, 0x5a, 0x50, 0x41, 0x60, 0x08, 0x95, - 0x9e, 0xe2, 0x28, 0xd0, 0x6f, 0x71, 0x5f, 0x0c, 0x1a, 0x2c, 0x59, 0x94, 0x71, 0x3f, 0x5e, 0x2e, 0x04, 0x35, 0xec, - 0xf3, 0xec, 0x65, 0x76, 0xce, 0xf2, 0x27, 0x11, 0xc2, 0x1e, 0x88, 0xee, 0x25, 0x48, 0x7a, 0x12, 0xe8, 0xac, 0xa7, - 0x78, 0xe5, 0x8c, 0x90, 0x86, 0x85, 0x98, 0xc6, 0xa8, 0x08, 0x41, 0xcb, 0x11, 0xed, 0x53, 0xdc, 0x52, 0xb4, 0xf7, - 0x50, 0x95, 0x30, 0xcd, 0x5b, 0xfb, 0x2f, 0xeb, 0xbc, 0x05, 0x23, 0xcc, 0x14, 0xb7, 0xd6, 0x77, 0xac, 0xeb, 0x49, - 0xdd, 0xec, 0x48, 0xde, 0x32, 0x94, 0x19, 0xe8, 0x8f, 0xab, 0xab, 0xca, 0x48, 0x07, 0x65, 0xaa, 0xa5, 0x39, 0x5a, - 0x4e, 0x62, 0x4b, 0xb8, 0x25, 0x28, 0x23, 0x34, 0xbc, 0xf2, 0x2c, 0x49, 0x0c, 0x5d, 0xe4, 0xc5, 0x3d, 0xa7, 0xa1, - 0x8e, 0x00, 0x8a, 0x69, 0x4d, 0x23, 0xf5, 0x79, 0xa0, 0x2b, 0x50, 0x29, 0x29, 0x6d, 0xe4, 0x55, 0x4d, 0x04, 0xc4, - 0xe9, 0x88, 0xe5, 0xc2, 0x41, 0x93, 0x3a, 0x14, 0x26, 0x4c, 0x81, 0xa1, 0xd9, 0x08, 0x24, 0xbc, 0x42, 0x00, 0xcc, - 0x13, 0x7f, 0x92, 0x15, 0x5c, 0xd7, 0x99, 0xd0, 0xc7, 0x57, 0x57, 0xb1, 0xf0, 0x17, 0x91, 0x01, 0x72, 0x36, 0xcd, - 0xce, 0xd8, 0x0a, 0xa8, 0x7b, 0x6a, 0x30, 0x13, 0x64, 0x63, 0x18, 0x50, 0xa2, 0xa0, 0x5a, 0x66, 0x49, 0x3c, 0x64, - 0x5a, 0x4b, 0x4d, 0x7d, 0x30, 0xe8, 0xd8, 0x05, 0xc8, 0x08, 0xe6, 0xee, 0xed, 0xed, 0xb5, 0xbd, 0x8e, 0x5b, 0x0a, - 0x82, 0x2f, 0x96, 0x28, 0x7a, 0x83, 0x7e, 0x94, 0x26, 0xf8, 0x2a, 0x59, 0xc0, 0x5d, 0x43, 0x29, 0x72, 0xe1, 0x27, - 0x79, 0x52, 0x10, 0xbb, 0xde, 0x08, 0x06, 0xe5, 0x4c, 0x09, 0x6e, 0x34, 0x71, 0xc5, 0xb6, 0x7d, 0xa7, 0xc9, 0xa6, - 0xd9, 0x49, 0xed, 0x30, 0xb5, 0x30, 0x72, 0xcd, 0x0b, 0xed, 0x01, 0x9b, 0xcb, 0x83, 0x56, 0x22, 0x55, 0x03, 0xaf, - 0x03, 0x84, 0xc2, 0xd3, 0x75, 0x96, 0x50, 0xaa, 0x3a, 0x4b, 0x21, 0xae, 0x37, 0xd0, 0x5b, 0x26, 0xc1, 0x5c, 0x45, - 0x82, 0x03, 0x29, 0x10, 0x38, 0x7a, 0x64, 0x62, 0xbd, 0x9e, 0xc0, 0xf2, 0x9c, 0x44, 0xc3, 0x4f, 0x1a, 0xdc, 0x8a, - 0xec, 0x4d, 0x36, 0x70, 0x1a, 0x25, 0xa1, 0x21, 0xae, 0x4c, 0xbc, 0x95, 0x84, 0xae, 0x6d, 0x14, 0x70, 0xc8, 0x96, - 0xd8, 0xbe, 0xb9, 0xd0, 0x4d, 0x6e, 0x97, 0xec, 0xa1, 0xfc, 0x27, 0xcd, 0x25, 0x37, 0xb0, 0x1c, 0x57, 0xd2, 0x80, - 0x2b, 0xc6, 0x83, 0xa5, 0x69, 0x40, 0x02, 0x7c, 0x57, 0x8e, 0xe2, 0xe2, 0x7a, 0x12, 0xfc, 0xa9, 0x60, 0x3e, 0x35, - 0x66, 0xba, 0x15, 0x52, 0x2d, 0xe1, 0xa4, 0x19, 0xac, 0x41, 0x93, 0xc6, 0x83, 0x12, 0x35, 0xdf, 0xa2, 0xa1, 0x42, - 0x1c, 0x7f, 0x2a, 0xaa, 0xd0, 0x04, 0x43, 0x30, 0x72, 0xaf, 0x90, 0x0c, 0x97, 0x2d, 0x8b, 0x16, 0x29, 0x53, 0x63, - 0x52, 0xa9, 0x9a, 0xe5, 0x32, 0x30, 0xb0, 0x68, 0xb7, 0xfa, 0xd2, 0x12, 0x57, 0x22, 0x37, 0x0d, 0xb5, 0x30, 0x29, - 0x94, 0x37, 0xe1, 0xe4, 0xe8, 0x77, 0x29, 0xeb, 0xdd, 0xc4, 0x27, 0x57, 0xf8, 0xe4, 0xbe, 0xe1, 0x43, 0x99, 0xbc, - 0x5d, 0xf4, 0x8b, 0xe0, 0x9b, 0x5a, 0x25, 0xda, 0xa7, 0x3e, 0x0a, 0x66, 0x57, 0x0b, 0x5d, 0x10, 0x28, 0x92, 0x4d, - 0xd2, 0xbe, 0xe4, 0x37, 0x14, 0x1b, 0x95, 0x67, 0x94, 0xb9, 0x62, 0x83, 0xd4, 0xbc, 0xd2, 0xcc, 0x4b, 0xdd, 0x86, - 0xfd, 0x5e, 0x96, 0x92, 0x4e, 0x5c, 0x50, 0x26, 0xf6, 0x6e, 0xa2, 0x8d, 0x97, 0x86, 0x99, 0xb0, 0x7e, 0x85, 0xb1, - 0x53, 0xa3, 0x50, 0x2a, 0x45, 0x20, 0x8e, 0x8d, 0xaf, 0x95, 0x65, 0x90, 0xf9, 0x2b, 0xec, 0x29, 0x00, 0x25, 0x81, - 0xc5, 0xd7, 0x54, 0xf2, 0xa2, 0xb0, 0x4e, 0xc7, 0x35, 0xa2, 0x63, 0x25, 0x42, 0x6b, 0x22, 0x5f, 0xeb, 0xb3, 0xd8, - 0xaf, 0xb9, 0x84, 0x26, 0x25, 0xf3, 0x7e, 0x1e, 0xd8, 0x2a, 0x10, 0x51, 0xe9, 0xb6, 0xa4, 0x9f, 0x90, 0x43, 0xba, - 0x4c, 0xf4, 0xda, 0x48, 0x06, 0xad, 0x53, 0x21, 0xd1, 0xd2, 0x41, 0x18, 0x39, 0xe8, 0xb8, 0xd3, 0x5a, 0x2c, 0x11, - 0xb2, 0x69, 0x6f, 0x12, 0x2b, 0xa2, 0x73, 0x9a, 0xa3, 0x09, 0x67, 0xea, 0x74, 0xc7, 0x01, 0x74, 0x40, 0xec, 0x2f, - 0xb1, 0xde, 0x4a, 0xb3, 0xd3, 0xf5, 0x2b, 0x87, 0xef, 0xea, 0x2a, 0x41, 0x7e, 0x10, 0x06, 0x2f, 0xac, 0x59, 0x5f, - 0xc9, 0xde, 0xfd, 0x97, 0xd8, 0x8a, 0xec, 0xcf, 0xaa, 0xa4, 0xf2, 0x14, 0x6a, 0x9c, 0x5b, 0x5f, 0x27, 0x66, 0x86, - 0x16, 0x55, 0xc5, 0x81, 0x21, 0xd5, 0x0f, 0x94, 0xc2, 0xae, 0x50, 0x3e, 0x90, 0x43, 0xc7, 0xae, 0xeb, 0x06, 0x39, - 0x39, 0x2f, 0x6b, 0xab, 0x5c, 0xc8, 0x8d, 0x0d, 0xd3, 0x67, 0x3a, 0xd3, 0xc3, 0x3f, 0x71, 0x50, 0x39, 0x17, 0x97, - 0x29, 0x59, 0x30, 0x4f, 0x94, 0x3a, 0x5a, 0x72, 0x40, 0xdb, 0x3d, 0xf4, 0xb4, 0xa3, 0xf3, 0x28, 0xe6, 0x96, 0x1e, - 0x45, 0x78, 0xda, 0x28, 0x9f, 0xa4, 0xd1, 0x01, 0x78, 0xa1, 0x09, 0x49, 0x4e, 0xb8, 0x69, 0x8b, 0x16, 0xc3, 0x09, - 0xc3, 0x10, 0xb8, 0xb2, 0x27, 0x4c, 0xd9, 0xb3, 0x86, 0x78, 0x8b, 0x81, 0xd9, 0x6a, 0xd8, 0xcb, 0x66, 0xf7, 0x9a, - 0xf9, 0x0f, 0x6b, 0x04, 0xb2, 0x6d, 0xaa, 0xea, 0xca, 0xc6, 0xbb, 0x14, 0x91, 0x18, 0x61, 0x5b, 0x35, 0xb6, 0xb4, - 0xf5, 0x7b, 0x0d, 0xf7, 0xba, 0x72, 0xcc, 0x6b, 0x4a, 0xb5, 0xa1, 0x87, 0x95, 0x9b, 0xc3, 0x4c, 0x47, 0x5e, 0xac, - 0xa0, 0xdb, 0x13, 0x41, 0x21, 0x70, 0x22, 0xb4, 0x3d, 0xa8, 0xb8, 0x81, 0x48, 0xc9, 0x95, 0x56, 0xcd, 0xe6, 0xc9, - 0x48, 0x02, 0x0b, 0x2e, 0x2c, 0x97, 0x7c, 0x74, 0x1e, 0x27, 0x49, 0x55, 0xfa, 0xa7, 0x0a, 0x78, 0x31, 0xec, 0x6d, - 0xa2, 0x5d, 0x60, 0x34, 0x57, 0x20, 0xb8, 0xda, 0x08, 0x7b, 0xef, 0xb8, 0xd5, 0xba, 0x8b, 0x88, 0x23, 0x37, 0xa3, - 0x11, 0x50, 0x8f, 0x11, 0x56, 0xcd, 0xda, 0xfb, 0xcf, 0x30, 0xa4, 0x66, 0xe0, 0x83, 0xea, 0x8c, 0x8a, 0x3f, 0xcb, - 0x9e, 0xfa, 0x4c, 0xf4, 0x6e, 0x55, 0x5d, 0xcd, 0x80, 0x8a, 0x0a, 0x7c, 0x98, 0x21, 0x96, 0xb6, 0x0a, 0x04, 0xe4, - 0x7a, 0x58, 0x94, 0x02, 0x26, 0x69, 0xb0, 0xa0, 0x14, 0x58, 0x6b, 0x65, 0xf7, 0xf2, 0xb6, 0x60, 0x0e, 0x85, 0xc2, - 0x45, 0xff, 0x27, 0xd9, 0x74, 0x86, 0x96, 0x59, 0x83, 0xa9, 0xa1, 0xc1, 0xfb, 0x46, 0x7d, 0xb9, 0xa2, 0xac, 0xd6, - 0x87, 0x76, 0x64, 0x8d, 0x9f, 0xb4, 0xa3, 0x0c, 0x0e, 0xd5, 0x5c, 0x17, 0xd5, 0xed, 0xe6, 0xa6, 0x88, 0x59, 0xc5, - 0xe3, 0x3e, 0xe9, 0x6d, 0x6d, 0x4d, 0x7a, 0x9a, 0x06, 0x24, 0x93, 0x24, 0xc3, 0x9b, 0x0c, 0x50, 0x56, 0xc4, 0x59, - 0x94, 0x0d, 0xf2, 0x2d, 0xca, 0x12, 0xd7, 0xef, 0xbb, 0xde, 0x7e, 0xcd, 0xb3, 0xf6, 0xf6, 0xaf, 0x77, 0x91, 0xab, - 0x3a, 0xe9, 0x41, 0x1e, 0x0d, 0xa0, 0x68, 0xc9, 0xa6, 0x0c, 0x17, 0xd3, 0x6c, 0xc4, 0x02, 0x1b, 0xba, 0xa7, 0x76, - 0xa9, 0xb4, 0x32, 0x6c, 0x8e, 0x94, 0x39, 0x8b, 0x77, 0xf5, 0x48, 0x6a, 0xb0, 0x0f, 0x2c, 0xa0, 0xcd, 0x85, 0xef, - 0xc3, 0xd3, 0x24, 0x3b, 0x89, 0x92, 0x43, 0xa1, 0xc0, 0x6b, 0x2d, 0x3f, 0x80, 0xcb, 0x48, 0x16, 0xab, 0xa1, 0xa4, - 0xbe, 0xef, 0x7f, 0x1f, 0xdc, 0xde, 0xa3, 0xf2, 0x56, 0xec, 0x8e, 0xdf, 0xf6, 0x3b, 0xb6, 0x8a, 0x88, 0xbd, 0x34, - 0xa7, 0x03, 0x8d, 0x53, 0x00, 0x65, 0x0e, 0x41, 0x93, 0x15, 0x5e, 0xc4, 0xc2, 0x97, 0xfd, 0x97, 0xca, 0xa5, 0xce, - 0xc0, 0x85, 0x00, 0x27, 0x3f, 0x89, 0x79, 0x0b, 0xcf, 0x23, 0x6d, 0x6f, 0x21, 0x2a, 0x30, 0xae, 0x48, 0x71, 0xe9, - 0x52, 0x79, 0x83, 0xde, 0xc7, 0xf0, 0x18, 0x9a, 0xad, 0xaf, 0x2f, 0x9c, 0x57, 0x11, 0x9f, 0xf8, 0x79, 0x94, 0x8e, - 0xb2, 0xa9, 0xe3, 0x6e, 0xda, 0xb6, 0xeb, 0x17, 0xe4, 0x89, 0x7c, 0xe5, 0x96, 0xeb, 0xc7, 0xde, 0x84, 0x85, 0x76, - 0xdf, 0xde, 0xfc, 0xe8, 0xbd, 0x67, 0xe1, 0xf1, 0xee, 0xfa, 0x62, 0xc2, 0xca, 0xbd, 0x63, 0xef, 0x42, 0xc7, 0xdc, - 0xbd, 0xb7, 0x28, 0x65, 0xa0, 0x57, 0xb8, 0x77, 0x21, 0xc1, 0x00, 0x76, 0xa3, 0xf8, 0x3b, 0x48, 0xb9, 0xf7, 0x74, - 0x20, 0x22, 0xe3, 0xb4, 0x57, 0x57, 0x76, 0x46, 0x11, 0x03, 0x7b, 0x4d, 0x3b, 0xab, 0x1b, 0x1b, 0x95, 0x9a, 0xaf, - 0x4a, 0xbd, 0x21, 0x0b, 0x6b, 0x9e, 0xba, 0x77, 0x48, 0x47, 0x2b, 0xf5, 0x8d, 0x3c, 0x64, 0xa4, 0x34, 0x97, 0xed, - 0x04, 0xc7, 0xd8, 0xe2, 0xab, 0xb7, 0xf5, 0x91, 0x88, 0x52, 0xf8, 0x31, 0x58, 0x2f, 0x11, 0xa8, 0x6f, 0x70, 0x70, - 0xbc, 0xc3, 0x70, 0x6b, 0xd7, 0xe9, 0x07, 0xce, 0x5a, 0xab, 0x75, 0xf5, 0xf3, 0xd6, 0xd1, 0x2f, 0x51, 0xeb, 0xf7, - 0xfd, 0xd6, 0x4f, 0x03, 0xf7, 0xca, 0xf9, 0x79, 0xab, 0x7f, 0x24, 0xdf, 0x8e, 0x7e, 0xd9, 0xfb, 0xb9, 0x18, 0x7c, - 0x29, 0x0a, 0xd7, 0x5d, 0x77, 0xeb, 0x14, 0x3c, 0xa5, 0x70, 0xab, 0xd5, 0xda, 0x83, 0xa7, 0x39, 0x3c, 0xe1, 0xcf, - 0x73, 0xf8, 0x71, 0x75, 0x64, 0xfd, 0x9b, 0x9f, 0xd3, 0x7f, 0xfb, 0x73, 0x3e, 0xc0, 0x31, 0x8f, 0x7e, 0xf9, 0xb9, - 0xb0, 0xef, 0xed, 0x85, 0x5b, 0x83, 0x4d, 0xd7, 0xd1, 0x35, 0x5f, 0x86, 0xd5, 0x23, 0xb4, 0x3a, 0xfa, 0x45, 0xbe, - 0xd9, 0xf7, 0x8e, 0x77, 0xf7, 0xc2, 0xc1, 0x95, 0x63, 0x5f, 0xdd, 0x73, 0xaf, 0x5c, 0xf7, 0x6a, 0x1d, 0xe7, 0x99, - 0xc1, 0xe8, 0xf7, 0xe0, 0xe7, 0x29, 0xfc, 0xb4, 0xe1, 0xe7, 0x19, 0xfc, 0xfc, 0x05, 0xba, 0x89, 0xf8, 0xdb, 0x15, - 0xc5, 0x42, 0xae, 0xf0, 0xc0, 0x22, 0x82, 0x55, 0x70, 0xd7, 0xb7, 0x62, 0x6f, 0x4c, 0x44, 0x83, 0x7d, 0xe8, 0xfb, - 0x3e, 0x86, 0x49, 0x9d, 0xc5, 0xc7, 0x75, 0x58, 0x74, 0xe4, 0x9c, 0xf5, 0x80, 0x79, 0x22, 0x72, 0x50, 0x04, 0x5c, - 0x9c, 0xad, 0x16, 0x78, 0xb8, 0xea, 0x8d, 0xc2, 0x31, 0x73, 0xc0, 0x28, 0x78, 0xce, 0xf0, 0xa1, 0xeb, 0x7a, 0xcf, - 0xe4, 0x99, 0x21, 0xee, 0x73, 0xc1, 0x5a, 0x69, 0x26, 0x4c, 0x1a, 0xdb, 0xf5, 0x66, 0x2b, 0x2a, 0x61, 0x5b, 0xa7, - 0xa7, 0x50, 0xb7, 0x2e, 0x0e, 0xda, 0xbe, 0x67, 0xd1, 0x27, 0xdc, 0x92, 0xaf, 0x8d, 0x43, 0xe0, 0x25, 0x4b, 0xbe, - 0x69, 0x34, 0x1a, 0x36, 0xa2, 0x70, 0xc7, 0x1e, 0x33, 0x98, 0x61, 0xc9, 0x44, 0xe4, 0xa4, 0x34, 0x85, 0x65, 0x0b, - 0x93, 0xbf, 0x8d, 0x72, 0xbe, 0x5e, 0x19, 0xb6, 0x61, 0xcd, 0x92, 0x6d, 0x5a, 0xfa, 0x77, 0x98, 0x02, 0x4d, 0x4b, - 0x3a, 0xff, 0x30, 0xc7, 0x0f, 0x53, 0x42, 0xeb, 0xb5, 0xc3, 0xc1, 0x43, 0x2f, 0x40, 0xee, 0x88, 0x7e, 0xce, 0x5b, - 0x54, 0x63, 0xf0, 0x57, 0x86, 0x19, 0x3c, 0x31, 0x1f, 0x86, 0x68, 0x16, 0xa5, 0x0e, 0x6e, 0xa5, 0x28, 0xee, 0x5f, - 0xe0, 0xce, 0x48, 0x4b, 0xef, 0x20, 0x54, 0x3b, 0xe6, 0x30, 0x67, 0xec, 0xfb, 0x28, 0xf9, 0xc4, 0x72, 0xe7, 0xc2, - 0xeb, 0x74, 0xbf, 0xa2, 0xce, 0x1e, 0xda, 0x66, 0xaf, 0xaa, 0x63, 0x34, 0x65, 0x16, 0xa8, 0x23, 0xc2, 0x56, 0xc7, - 0xcb, 0x31, 0xaa, 0x85, 0x24, 0x28, 0xbc, 0x2c, 0xec, 0x12, 0x87, 0xdb, 0xbb, 0xc5, 0xd9, 0xe9, 0x9e, 0x1d, 0xd8, - 0x36, 0x58, 0xfc, 0x87, 0x14, 0xb6, 0x12, 0x86, 0x05, 0x18, 0x64, 0xbb, 0x71, 0x8f, 0x6f, 0x6e, 0x56, 0x01, 0x27, - 0x3c, 0x48, 0xa7, 0xee, 0x89, 0x17, 0x79, 0x93, 0x10, 0x06, 0x1c, 0x42, 0x33, 0xec, 0xd2, 0x1b, 0xee, 0xc6, 0x72, - 0x1a, 0x8c, 0x85, 0xf8, 0x49, 0x54, 0xf0, 0x17, 0x18, 0x8f, 0x08, 0x87, 0x68, 0xec, 0xfb, 0xec, 0x82, 0x0d, 0x95, - 0x9d, 0x01, 0x84, 0x8a, 0xdc, 0x9e, 0x3b, 0x0c, 0x8d, 0x66, 0x30, 0x77, 0x18, 0x1e, 0xf6, 0x6d, 0xd8, 0x4b, 0xb0, - 0x2b, 0xc3, 0xe8, 0xa8, 0x33, 0xe8, 0xa7, 0x21, 0xc8, 0x5a, 0x4d, 0x5b, 0x59, 0x34, 0xaf, 0x15, 0x75, 0x07, 0x7d, - 0xe7, 0x0c, 0x8c, 0x74, 0xb0, 0xc5, 0x1d, 0x7c, 0xc3, 0x08, 0x45, 0x11, 0xbe, 0x63, 0xa7, 0xcf, 0x2e, 0x66, 0x8e, - 0xbd, 0xbb, 0x65, 0x6f, 0x62, 0xa9, 0x67, 0x03, 0x7b, 0xc1, 0xdc, 0xe1, 0xb9, 0x6b, 0x76, 0xde, 0x1e, 0x20, 0xa8, - 0x58, 0x88, 0x93, 0x9f, 0xf7, 0xed, 0x3d, 0x31, 0x75, 0x1b, 0x06, 0x4d, 0xe5, 0xf2, 0xe3, 0x8a, 0x1e, 0x12, 0xaa, - 0xaa, 0xab, 0x82, 0x0e, 0xca, 0xba, 0x81, 0x33, 0x31, 0x91, 0x68, 0xe1, 0x64, 0x92, 0x0a, 0xe0, 0xf0, 0x60, 0x33, - 0x98, 0xd4, 0xe8, 0xb6, 0x3d, 0xe8, 0x9f, 0x07, 0xf7, 0xec, 0x7b, 0xea, 0xe5, 0x94, 0x05, 0xe0, 0x5d, 0xd0, 0xf4, - 0xa7, 0xa8, 0x45, 0xe0, 0xe7, 0x8c, 0x01, 0x92, 0xe7, 0x54, 0x34, 0x92, 0x45, 0x73, 0x2c, 0x3a, 0x0c, 0x10, 0x54, - 0xaf, 0xd0, 0xd6, 0x9f, 0x58, 0x93, 0x51, 0x48, 0xb0, 0x6f, 0x6c, 0xc0, 0xd2, 0x6c, 0x76, 0x06, 0x78, 0xde, 0x90, - 0xf3, 0xe2, 0xfb, 0x98, 0x83, 0x4a, 0xd8, 0xda, 0xb3, 0xdd, 0xbe, 0x6d, 0xe1, 0xd2, 0xf6, 0xb2, 0xcd, 0x50, 0x50, - 0x38, 0xde, 0x7c, 0xcf, 0x82, 0xc9, 0x5e, 0xd8, 0xee, 0x3b, 0xb9, 0x50, 0x1d, 0x09, 0x9e, 0x5b, 0x0a, 0x09, 0xde, - 0xf6, 0x26, 0x20, 0xd0, 0x91, 0x73, 0xd7, 0xed, 0x4d, 0x55, 0x08, 0x45, 0x1f, 0x37, 0x47, 0x6e, 0x10, 0xc3, 0x0f, - 0xa7, 0x85, 0x4c, 0x33, 0xd1, 0x7d, 0xb5, 0x66, 0x76, 0x83, 0x91, 0xb2, 0xc8, 0x93, 0x30, 0xdb, 0x74, 0x30, 0x42, - 0x0b, 0x92, 0x76, 0xb7, 0x0f, 0x30, 0x6c, 0x3a, 0x8a, 0xd3, 0xb6, 0x14, 0xab, 0x29, 0xfb, 0xfc, 0xa8, 0x5a, 0x0e, - 0xd6, 0x8f, 0x98, 0x5f, 0x69, 0x1f, 0x00, 0x2b, 0x48, 0xbc, 0x7c, 0xa0, 0xce, 0xbc, 0x9e, 0xd7, 0xce, 0xb7, 0x16, - 0x4a, 0x14, 0x31, 0xcf, 0x90, 0x50, 0xbc, 0xd4, 0x6e, 0x98, 0x30, 0xb7, 0x67, 0x48, 0x0c, 0xcd, 0xf2, 0x61, 0x1b, - 0x98, 0x5e, 0x05, 0xd8, 0x53, 0x73, 0x5b, 0x24, 0x61, 0xd5, 0xdc, 0x3b, 0x02, 0xd6, 0x1e, 0x84, 0xaf, 0xc4, 0x89, - 0x63, 0x4f, 0x45, 0xf3, 0x59, 0x12, 0x3e, 0x6f, 0x1c, 0x17, 0x47, 0x78, 0x22, 0x74, 0xe0, 0x0f, 0xe7, 0x39, 0xc8, - 0x03, 0xfe, 0x1a, 0x2c, 0x83, 0x50, 0x36, 0x45, 0x47, 0x0f, 0x8f, 0x80, 0x3d, 0x42, 0xbc, 0x11, 0x36, 0x37, 0xaa, - 0xd1, 0xa2, 0x24, 0xe3, 0x85, 0x0e, 0x86, 0x7b, 0x5c, 0xba, 0xf6, 0x28, 0x18, 0xe4, 0x89, 0xb1, 0x83, 0x67, 0xfe, - 0xfe, 0x10, 0xab, 0x71, 0x82, 0xc2, 0x2d, 0x69, 0xb7, 0x55, 0xe2, 0xef, 0xc0, 0x4f, 0x41, 0x82, 0x63, 0x1d, 0xf8, - 0x59, 0x1b, 0x1b, 0x89, 0x44, 0x6a, 0x37, 0xed, 0xd1, 0x49, 0x04, 0xc6, 0x83, 0x73, 0x3f, 0x85, 0x6a, 0x24, 0x11, - 0x15, 0xe5, 0x68, 0x81, 0x9a, 0xa7, 0x6a, 0x15, 0x7c, 0x47, 0x66, 0x04, 0x9e, 0x63, 0xd8, 0x9a, 0xfc, 0x54, 0xdd, - 0x58, 0xc4, 0xf2, 0x5d, 0x97, 0x8e, 0xb6, 0xf0, 0x00, 0x52, 0x30, 0x9a, 0x60, 0x18, 0x97, 0x82, 0x92, 0x15, 0xff, - 0x7d, 0x34, 0x62, 0xe5, 0x93, 0xa3, 0x6c, 0x73, 0x73, 0x20, 0xce, 0x2d, 0x88, 0x71, 0xb8, 0x11, 0x5d, 0x8d, 0x2b, - 0x00, 0xea, 0xd3, 0x39, 0x71, 0x3d, 0x30, 0xad, 0x58, 0xd3, 0xa5, 0xd8, 0x27, 0x87, 0x19, 0x80, 0x82, 0x5b, 0xce, - 0x91, 0xdf, 0xff, 0xcb, 0x00, 0xdc, 0x63, 0xff, 0x4b, 0x77, 0x4b, 0x09, 0x9a, 0x9e, 0x3c, 0x53, 0x5c, 0xd0, 0x19, - 0x6b, 0xc7, 0xa3, 0xd8, 0x68, 0x50, 0x78, 0x29, 0x60, 0x00, 0xda, 0x1c, 0x64, 0x42, 0xc5, 0x41, 0xc8, 0x51, 0x81, - 0xed, 0xe3, 0xe6, 0xe7, 0xb8, 0xb3, 0x9f, 0x82, 0x85, 0xd7, 0xd7, 0x6f, 0x8f, 0xe1, 0xed, 0x2f, 0xfa, 0xed, 0x25, - 0x0b, 0x7e, 0x2d, 0x65, 0xe8, 0xbe, 0x36, 0xc5, 0x03, 0x35, 0x45, 0x29, 0x96, 0xc8, 0xa0, 0x21, 0x73, 0xf3, 0xa5, - 0x98, 0x0d, 0x77, 0x4b, 0x54, 0x3b, 0x52, 0x74, 0xe5, 0x3e, 0x8f, 0x4e, 0x91, 0xb8, 0xae, 0x49, 0x0a, 0x23, 0x97, - 0xc0, 0x44, 0xb8, 0xe2, 0x5b, 0x62, 0xce, 0xbd, 0x36, 0xd8, 0xe0, 0xb5, 0xbc, 0x03, 0xb4, 0xef, 0xd8, 0x74, 0xc6, - 0x2f, 0x0f, 0x48, 0xd1, 0x07, 0x32, 0x6d, 0x40, 0x9c, 0x9d, 0xb7, 0x7b, 0xf1, 0x2e, 0xef, 0xc5, 0x20, 0xd5, 0x73, - 0xc5, 0x62, 0xb8, 0x57, 0xbd, 0xb7, 0x18, 0xa5, 0x34, 0x99, 0xc9, 0xab, 0xa1, 0xd7, 0x95, 0xe8, 0x6d, 0x6e, 0x02, - 0x82, 0x3d, 0xa3, 0x2b, 0x17, 0x5d, 0xcb, 0x52, 0xd0, 0x04, 0x20, 0x7a, 0x54, 0x67, 0x39, 0xe2, 0x38, 0xcc, 0x66, - 0x43, 0xc1, 0xc1, 0xdc, 0x95, 0xa3, 0xe2, 0x98, 0xd8, 0x5d, 0x26, 0xec, 0x00, 0x66, 0xc4, 0xe5, 0xad, 0x8e, 0x88, - 0x0e, 0x8b, 0xfe, 0x3a, 0xbe, 0xfd, 0xd1, 0x63, 0x9b, 0x1d, 0x17, 0x34, 0x48, 0x6d, 0xac, 0x87, 0xd5, 0x58, 0x50, - 0x1f, 0x7e, 0xd4, 0x54, 0x2a, 0x8b, 0xcd, 0xcd, 0xb2, 0x7e, 0x54, 0xab, 0x76, 0x70, 0xed, 0x34, 0xe5, 0xa2, 0x99, - 0x0d, 0xc2, 0x81, 0x88, 0x09, 0x14, 0x68, 0x69, 0x65, 0xc5, 0x00, 0x43, 0xca, 0x72, 0x94, 0x4f, 0x21, 0xf3, 0xe2, - 0xb2, 0xd4, 0xa9, 0x2f, 0x32, 0x1e, 0x19, 0xe2, 0xa9, 0x27, 0x19, 0x2b, 0xa0, 0x60, 0xbd, 0xd4, 0x4b, 0x68, 0x89, - 0x00, 0xf3, 0x67, 0x2a, 0x87, 0x46, 0x58, 0x20, 0x51, 0x68, 0x98, 0x25, 0xca, 0xf8, 0x2c, 0xc2, 0x18, 0xb4, 0xfd, - 0x93, 0x5a, 0xec, 0xab, 0x50, 0x46, 0x47, 0x71, 0x94, 0x0f, 0x02, 0xaa, 0x9f, 0x4b, 0x09, 0x36, 0x09, 0xdf, 0x03, - 0x1b, 0x55, 0x8e, 0x27, 0x09, 0xc2, 0xa7, 0x71, 0xce, 0xc8, 0x53, 0x58, 0x97, 0x30, 0x4b, 0xd3, 0x36, 0x52, 0xed, - 0x22, 0x33, 0x08, 0xe5, 0xc2, 0xfc, 0x13, 0xe3, 0xec, 0x22, 0x0b, 0x97, 0x5a, 0x83, 0xf9, 0xf1, 0xc6, 0x04, 0x28, - 0xbb, 0xba, 0xca, 0x84, 0x8f, 0x1b, 0x91, 0xbd, 0xa1, 0x2b, 0x26, 0x7d, 0x85, 0x54, 0xe0, 0x44, 0x64, 0xf1, 0xd0, - 0x19, 0x0a, 0x8d, 0x70, 0x48, 0xa7, 0xc8, 0xb9, 0x6b, 0x6c, 0xfa, 0xbc, 0xaf, 0x7d, 0xa3, 0x34, 0x74, 0x12, 0x10, - 0x02, 0x02, 0x77, 0xc3, 0x9a, 0x4a, 0xfb, 0x69, 0x90, 0x50, 0x29, 0xfa, 0x39, 0x80, 0x7f, 0x18, 0x49, 0x0a, 0x80, - 0xfd, 0x50, 0x8d, 0x14, 0x51, 0x96, 0x05, 0x2e, 0x00, 0xcd, 0x75, 0x80, 0x2b, 0xe1, 0x0b, 0x03, 0x15, 0xa6, 0xa7, - 0x59, 0x79, 0x29, 0x94, 0xc8, 0xbb, 0x15, 0x29, 0x6b, 0x24, 0x93, 0xcf, 0xd0, 0xe1, 0x53, 0xde, 0xf5, 0x6b, 0x89, - 0x87, 0x2e, 0x78, 0x0a, 0xcb, 0xaa, 0x9e, 0x5f, 0x85, 0x9c, 0x9c, 0x6b, 0xd0, 0x15, 0x52, 0xe8, 0x2f, 0x39, 0xc9, - 0xfb, 0xaf, 0xfc, 0xaa, 0x96, 0x1a, 0x43, 0xd9, 0xfb, 0x55, 0xcd, 0xb0, 0xbc, 0x9c, 0x55, 0x61, 0x0a, 0x02, 0x6e, - 0xc1, 0x92, 0x60, 0x21, 0x35, 0x04, 0x58, 0xd8, 0x1e, 0x69, 0xa5, 0x20, 0x2f, 0x75, 0x78, 0xe7, 0x29, 0x58, 0x01, - 0xc6, 0xa1, 0x96, 0x4a, 0xa6, 0x91, 0xc4, 0x97, 0x4a, 0x14, 0x98, 0xf2, 0x60, 0x08, 0x7e, 0x6a, 0xf3, 0xa4, 0xeb, - 0xc2, 0xf5, 0xe3, 0x29, 0xa6, 0xf6, 0x10, 0xe8, 0xb1, 0xb7, 0x06, 0xa6, 0x44, 0x5d, 0x87, 0x15, 0xc4, 0xa1, 0x59, - 0x4d, 0xb3, 0x80, 0x19, 0xd3, 0x06, 0x2d, 0xd9, 0x06, 0x5b, 0x2e, 0x07, 0xfb, 0x48, 0x6c, 0xcf, 0x6a, 0x05, 0x84, - 0xae, 0x41, 0x03, 0x43, 0xee, 0x52, 0xa1, 0x85, 0x79, 0xaf, 0x4b, 0x45, 0xb8, 0x3f, 0xfb, 0x5c, 0x5a, 0xc1, 0x99, - 0x97, 0xd1, 0xc0, 0x07, 0xf1, 0x49, 0x82, 0x89, 0x2f, 0x88, 0x15, 0xd8, 0xc1, 0x41, 0xa7, 0xd9, 0x14, 0x38, 0x15, - 0x17, 0x29, 0x83, 0x65, 0x45, 0xa9, 0x0d, 0x3f, 0xa4, 0xc8, 0xd6, 0x5d, 0x1e, 0xe8, 0x2e, 0xc4, 0x02, 0xd8, 0xe9, - 0x57, 0x8c, 0x7c, 0xcb, 0x7a, 0x19, 0x30, 0x38, 0xd3, 0x1a, 0x07, 0x81, 0xdf, 0xdc, 0x4c, 0x06, 0x65, 0x4a, 0x6c, - 0xd7, 0x64, 0x75, 0x01, 0x39, 0x0c, 0xd5, 0xc4, 0x1d, 0x84, 0xa5, 0xb2, 0xc7, 0x8b, 0x72, 0x8a, 0xcb, 0xa5, 0x2c, - 0xe4, 0xe6, 0x79, 0x35, 0xcd, 0xe7, 0x56, 0x9a, 0x4d, 0xc7, 0x5b, 0xf1, 0x45, 0xc1, 0xdf, 0x77, 0x62, 0x69, 0xd5, - 0x53, 0x6a, 0x85, 0x47, 0x99, 0x5b, 0xb2, 0x4e, 0x49, 0xad, 0xae, 0x1b, 0xa8, 0x46, 0x78, 0x9a, 0x86, 0x8d, 0x40, - 0x88, 0x09, 0x2e, 0x7e, 0xdd, 0x64, 0x62, 0xda, 0x5b, 0x42, 0xea, 0x08, 0xbb, 0x87, 0x72, 0x82, 0xbb, 0x9a, 0x67, - 0x9f, 0x87, 0xb3, 0xeb, 0x99, 0x7b, 0xdf, 0x60, 0xee, 0xc7, 0x21, 0x37, 0x18, 0x3d, 0x96, 0x09, 0x3f, 0x32, 0xf6, - 0x91, 0xab, 0xaa, 0x27, 0xa7, 0x61, 0x25, 0xb2, 0xc4, 0x93, 0x71, 0xd4, 0x61, 0x9c, 0x8a, 0xd6, 0x04, 0xd9, 0xd5, - 0x55, 0x61, 0xee, 0x05, 0x0a, 0x9a, 0x7a, 0xbc, 0x1e, 0xa7, 0xad, 0xd8, 0xd9, 0x88, 0x44, 0xee, 0xbf, 0xaa, 0x45, - 0x22, 0x2b, 0x3e, 0xc7, 0x91, 0xae, 0x39, 0xc8, 0x7d, 0x72, 0xba, 0xbc, 0x49, 0x85, 0x6e, 0xd1, 0x68, 0x1b, 0x7b, - 0x54, 0x1f, 0x48, 0xea, 0x19, 0x15, 0x58, 0xd5, 0xd8, 0x1b, 0x1b, 0x1d, 0x91, 0x6e, 0xa9, 0x14, 0x1b, 0x2c, 0x2d, - 0x8c, 0x66, 0x8c, 0x82, 0x41, 0x49, 0x91, 0x81, 0x1a, 0xe5, 0xd7, 0x08, 0x86, 0x7d, 0x6a, 0x00, 0x8a, 0x73, 0x75, - 0xf5, 0xe3, 0x52, 0xb2, 0x85, 0x80, 0xc4, 0x5d, 0x30, 0x10, 0x6b, 0x82, 0x99, 0x91, 0x4f, 0xde, 0x03, 0xe7, 0xf5, - 0x19, 0xfa, 0x08, 0xe0, 0x17, 0x88, 0x4d, 0x0f, 0x26, 0xb6, 0x4d, 0x44, 0xd1, 0x67, 0x03, 0xcf, 0x01, 0xd8, 0x59, - 0x15, 0x1a, 0x7d, 0x57, 0xa5, 0x80, 0x21, 0x1b, 0xb8, 0x01, 0xab, 0xc2, 0x72, 0xfb, 0xcf, 0xc1, 0x6d, 0x80, 0xd7, - 0x67, 0xb2, 0xf9, 0x3a, 0xe6, 0x09, 0x56, 0x67, 0x17, 0x7e, 0x65, 0x59, 0x8b, 0x73, 0xa7, 0xc3, 0x46, 0xbd, 0xa2, - 0x84, 0xa8, 0x3d, 0xc0, 0xda, 0x43, 0x8c, 0xb0, 0x88, 0xf7, 0x57, 0xf8, 0xae, 0xc7, 0x2d, 0xf7, 0x35, 0x5a, 0x84, - 0xe9, 0x32, 0x69, 0x0c, 0x4a, 0xd6, 0xfd, 0x64, 0xc4, 0xbd, 0x3c, 0x10, 0xb1, 0xe0, 0x0a, 0x47, 0x56, 0x85, 0x14, - 0x1b, 0x48, 0xd2, 0xd3, 0x3e, 0x1d, 0xb0, 0xaf, 0x37, 0x7b, 0x01, 0x65, 0xde, 0x57, 0xa4, 0x92, 0x90, 0xd2, 0xec, - 0x86, 0x48, 0x12, 0xd6, 0x8a, 0x3c, 0x75, 0x3e, 0x70, 0xb4, 0xcf, 0xad, 0x24, 0x82, 0x11, 0x9c, 0x84, 0xe9, 0x58, - 0x79, 0xd8, 0x14, 0xe0, 0x2a, 0x3a, 0x62, 0xfa, 0x26, 0x20, 0xbf, 0x19, 0xc8, 0xed, 0xa5, 0xe4, 0xda, 0x5c, 0xc3, - 0xf0, 0x04, 0x09, 0x56, 0x45, 0x22, 0xf0, 0x88, 0x1a, 0x70, 0xcc, 0x57, 0x79, 0x1e, 0x60, 0xc2, 0xd7, 0xf6, 0x26, - 0x00, 0x94, 0x93, 0xab, 0xe2, 0x2c, 0x05, 0xba, 0x01, 0xcb, 0xd5, 0x71, 0x6a, 0x54, 0x24, 0x2e, 0x6e, 0x4c, 0x57, - 0xb7, 0xf4, 0xa7, 0x68, 0x39, 0x93, 0x21, 0xa6, 0x83, 0x20, 0x20, 0x53, 0xdf, 0x31, 0x47, 0xc8, 0x5c, 0x61, 0x7d, - 0xce, 0x9c, 0xda, 0xd4, 0x3d, 0x46, 0xdd, 0x3c, 0x49, 0x2d, 0x5e, 0xa7, 0x4d, 0x29, 0x11, 0x93, 0x12, 0x73, 0x5d, - 0xa4, 0x62, 0x33, 0x25, 0xee, 0xdc, 0xfa, 0x46, 0x0b, 0x69, 0xa3, 0xad, 0x8b, 0x1c, 0x6c, 0x56, 0xc9, 0x7b, 0x02, - 0xe3, 0xb9, 0x20, 0x7c, 0xf9, 0x8a, 0x92, 0x74, 0x98, 0x63, 0x22, 0x58, 0xbd, 0x98, 0x8a, 0xfc, 0x9d, 0xa3, 0xd3, - 0xec, 0x0d, 0x7a, 0x90, 0x7a, 0x03, 0x89, 0x59, 0x13, 0xdf, 0x85, 0x34, 0xd4, 0x11, 0x02, 0x95, 0x51, 0x2d, 0xd3, - 0x71, 0x62, 0x15, 0xbe, 0x11, 0x7c, 0xf5, 0x56, 0x1f, 0xe7, 0x1b, 0xcf, 0x8d, 0xd5, 0x08, 0x62, 0xf0, 0x16, 0xf2, - 0x81, 0x27, 0x45, 0x38, 0x10, 0x2e, 0xdf, 0xdc, 0xec, 0xe5, 0xbb, 0xbc, 0x0a, 0x91, 0x54, 0x30, 0xc6, 0x98, 0x51, - 0x8c, 0x7b, 0xa2, 0xa6, 0x16, 0x73, 0x18, 0x58, 0xb6, 0x0e, 0x73, 0x3c, 0x00, 0x80, 0x96, 0xa6, 0xf4, 0xaa, 0xa9, - 0x50, 0x79, 0x9e, 0x4b, 0xf8, 0x54, 0x87, 0xa8, 0xaa, 0xf1, 0xdb, 0xd5, 0x19, 0x28, 0x04, 0xf7, 0x5a, 0xc7, 0xc3, - 0x43, 0x08, 0x58, 0x45, 0x21, 0x0b, 0xf4, 0x06, 0xed, 0x55, 0x89, 0x50, 0xcc, 0x9c, 0xac, 0xc7, 0x0c, 0x27, 0x15, - 0x6c, 0xa1, 0x12, 0x96, 0x4a, 0x0b, 0xfc, 0x6a, 0x23, 0x34, 0x4f, 0x19, 0xf7, 0x5f, 0x55, 0x38, 0x83, 0xfe, 0x60, - 0xde, 0x32, 0xa3, 0xbe, 0x5d, 0x3a, 0x91, 0xa9, 0xc0, 0xc4, 0xcd, 0x2c, 0xb5, 0xdf, 0xaf, 0xab, 0xb4, 0x9f, 0x57, - 0xc8, 0x7d, 0x4e, 0x9a, 0xaf, 0x73, 0x07, 0xcd, 0x27, 0xc3, 0xfd, 0x4a, 0xf9, 0xa1, 0x85, 0x51, 0x53, 0x7e, 0x79, - 0x5d, 0xf9, 0x15, 0x9e, 0x0a, 0x6f, 0xed, 0x75, 0x51, 0xe8, 0xa2, 0x3e, 0x07, 0x43, 0x48, 0x3f, 0x82, 0x6b, 0x68, - 0xf0, 0xa0, 0x48, 0x16, 0x8b, 0xb5, 0x0b, 0xe2, 0xfa, 0x98, 0x53, 0xed, 0x50, 0xc6, 0x18, 0xf1, 0xb4, 0xe4, 0x20, - 0xc9, 0xe0, 0x60, 0xfc, 0x06, 0x06, 0xc4, 0xa4, 0x24, 0xa4, 0x43, 0xe8, 0xac, 0xcc, 0x44, 0x54, 0xee, 0xe2, 0xed, - 0xc6, 0x65, 0x4d, 0xa1, 0x08, 0x3b, 0xc1, 0x4c, 0xa5, 0x54, 0x10, 0x48, 0x93, 0x6f, 0xad, 0x53, 0x0b, 0x86, 0x16, - 0xae, 0xa9, 0x80, 0xbc, 0xb6, 0xeb, 0x41, 0x93, 0xf7, 0x14, 0x43, 0x5f, 0xa5, 0x46, 0xbc, 0xcc, 0xe0, 0x6b, 0xd8, - 0xfc, 0x35, 0x51, 0x92, 0x87, 0x4c, 0xc4, 0x5e, 0xc1, 0x27, 0x42, 0x36, 0x05, 0x3b, 0x13, 0xe8, 0x87, 0x76, 0x65, - 0x2f, 0xdd, 0x2d, 0x2a, 0x97, 0x16, 0x8d, 0xad, 0x44, 0xcd, 0x9a, 0x1f, 0xc5, 0x9b, 0x29, 0xec, 0x67, 0x8f, 0x12, - 0x08, 0x48, 0x53, 0x39, 0x49, 0x35, 0xef, 0x51, 0x3a, 0x00, 0x90, 0x60, 0xf7, 0x13, 0x58, 0xe8, 0x37, 0x25, 0x26, - 0x58, 0x54, 0x8d, 0xdd, 0x66, 0xa0, 0x35, 0x67, 0xa4, 0xf9, 0x66, 0xa8, 0xb5, 0x37, 0x95, 0xf5, 0x8c, 0xd9, 0x01, - 0xb6, 0xed, 0x6e, 0x16, 0x47, 0xe9, 0x66, 0x67, 0x60, 0x08, 0x2e, 0x3c, 0xfe, 0x4f, 0x4a, 0x4c, 0x03, 0xc9, 0xa5, - 0x6e, 0xfc, 0x84, 0x3a, 0x0c, 0xff, 0x5b, 0x90, 0x02, 0xee, 0xd7, 0x56, 0x63, 0xc9, 0xb9, 0x57, 0x1c, 0x25, 0x97, - 0x55, 0xb5, 0xab, 0x25, 0x68, 0xe8, 0x46, 0x32, 0x26, 0x8a, 0x79, 0x4e, 0x00, 0x8c, 0x62, 0xf3, 0xa7, 0x4c, 0x27, - 0x79, 0xff, 0xba, 0x32, 0xb5, 0xdb, 0xf7, 0xfd, 0x28, 0x3f, 0xa5, 0x23, 0x15, 0x95, 0xcd, 0x49, 0xcc, 0xbf, 0x2d, - 0xc0, 0x34, 0x27, 0x3e, 0xd2, 0x73, 0x0d, 0x42, 0x01, 0xbe, 0xb2, 0xa1, 0xd4, 0x6c, 0x8f, 0xff, 0xe8, 0x6c, 0xf7, - 0x25, 0x51, 0x04, 0x0b, 0x34, 0xe8, 0x72, 0x0d, 0xbe, 0x80, 0x65, 0x70, 0x47, 0xfa, 0x29, 0xf8, 0x5e, 0x5e, 0x07, - 0x9f, 0xb1, 0xff, 0x05, 0xa0, 0x55, 0x81, 0x01, 0xe5, 0x4e, 0xd3, 0xb0, 0x12, 0xe2, 0x12, 0x15, 0x66, 0x15, 0xe7, - 0x8f, 0xeb, 0xbc, 0x6e, 0x5a, 0x96, 0x18, 0x94, 0x9f, 0xb9, 0x86, 0x1b, 0xdf, 0x6b, 0xe4, 0x8f, 0xef, 0x3f, 0x07, - 0xdd, 0x4e, 0xa4, 0xdd, 0xd8, 0xc8, 0xd7, 0xc8, 0x42, 0xc3, 0x7b, 0x61, 0x33, 0x68, 0x8b, 0x74, 0xc9, 0xd5, 0x33, - 0x16, 0xe3, 0x6d, 0x11, 0x2a, 0xc3, 0x07, 0x2c, 0x98, 0x01, 0x86, 0xe0, 0xb1, 0x53, 0x99, 0x7c, 0x86, 0x8d, 0xa6, - 0xd8, 0x35, 0x17, 0x06, 0x1f, 0xa8, 0xca, 0x42, 0xf2, 0x62, 0x9d, 0x6c, 0xcf, 0xce, 0xe0, 0xf9, 0x65, 0x5c, 0x00, - 0x75, 0x00, 0xfd, 0x8a, 0xca, 0x62, 0x03, 0xb9, 0xb8, 0x29, 0x6b, 0xbd, 0xa2, 0xd1, 0xe8, 0xc6, 0x2e, 0xac, 0xae, - 0xc0, 0x27, 0x51, 0x3a, 0x4a, 0xc4, 0x24, 0x66, 0x52, 0xe5, 0x8a, 0x5c, 0x1b, 0xdd, 0x4b, 0x5b, 0x34, 0xcf, 0x85, - 0x04, 0xaf, 0x08, 0xdc, 0x10, 0xfa, 0x4a, 0x5f, 0xae, 0x36, 0x50, 0xf0, 0xa8, 0xbd, 0xb9, 0x08, 0x26, 0x26, 0x1e, - 0x33, 0xa4, 0xa6, 0x5f, 0x87, 0x53, 0x2b, 0x8b, 0x25, 0x87, 0x5f, 0xe7, 0x8c, 0x35, 0x14, 0x00, 0xf1, 0xc9, 0x83, - 0xeb, 0xdd, 0xa4, 0x57, 0x4a, 0x3b, 0x28, 0x8d, 0x10, 0xdf, 0x55, 0xf8, 0xba, 0x0b, 0xc5, 0x57, 0xae, 0xba, 0xf7, - 0x35, 0x65, 0xc6, 0x05, 0xa3, 0xe7, 0x7c, 0x9a, 0x34, 0xae, 0xdd, 0xd0, 0x5d, 0x9d, 0x9f, 0xbc, 0x1f, 0x65, 0xde, - 0xc2, 0x14, 0xd8, 0x04, 0x54, 0xc1, 0x73, 0xef, 0xb5, 0x71, 0xa2, 0xfc, 0x9d, 0x79, 0xc4, 0x2b, 0x87, 0x59, 0x75, - 0x92, 0xfc, 0x5d, 0xff, 0xbb, 0xe0, 0xfa, 0x96, 0xc6, 0x09, 0x72, 0x57, 0x9d, 0x20, 0x13, 0xe5, 0xa6, 0x7a, 0xc3, - 0xed, 0xdf, 0x55, 0x20, 0x88, 0x53, 0x31, 0x7d, 0x54, 0x8e, 0xeb, 0x47, 0x0b, 0x54, 0x2a, 0x22, 0x3e, 0x55, 0xb9, - 0x2b, 0xd7, 0xa6, 0x86, 0x7a, 0x5c, 0x27, 0xb3, 0xd0, 0x34, 0x2b, 0x72, 0x29, 0x9b, 0x1e, 0x23, 0xd3, 0xec, 0x54, - 0x9b, 0xdf, 0xbd, 0xf6, 0x90, 0x8e, 0xa1, 0xb9, 0x58, 0xab, 0x05, 0xf7, 0xbb, 0x8a, 0xc2, 0xbb, 0x5e, 0x6c, 0xa4, - 0x32, 0xd4, 0xac, 0x47, 0xd1, 0xc7, 0x71, 0x9b, 0xb9, 0x3c, 0xca, 0xfe, 0xac, 0x01, 0x60, 0x3a, 0xc2, 0xa2, 0xbb, - 0xe9, 0x19, 0x7b, 0x02, 0x3d, 0x3d, 0x91, 0x41, 0xa2, 0xd7, 0x3a, 0x5f, 0xb5, 0x4a, 0x2c, 0x5d, 0x41, 0x60, 0xf7, - 0x86, 0x8c, 0x55, 0x49, 0xbb, 0xe5, 0xfa, 0xe5, 0x3c, 0x9f, 0xa7, 0x7c, 0x29, 0xcf, 0xa7, 0x66, 0xd1, 0xad, 0xb5, - 0xdd, 0x9b, 0x53, 0x43, 0xc5, 0x5c, 0xab, 0x9b, 0xfc, 0x81, 0xe9, 0x3a, 0x18, 0x6a, 0x11, 0x64, 0x56, 0xbb, 0xea, - 0x59, 0x59, 0x4e, 0xeb, 0x99, 0x1c, 0x53, 0xe1, 0x9b, 0x4a, 0x77, 0x88, 0x6e, 0x98, 0xaa, 0x99, 0x7e, 0x6a, 0x6c, - 0x0b, 0xd9, 0xe6, 0xf9, 0xe5, 0x28, 0x07, 0x4a, 0xcb, 0xfd, 0x65, 0xc2, 0xf0, 0xd3, 0xd5, 0xd5, 0x4f, 0x42, 0x4e, - 0x55, 0x1d, 0xbd, 0xc5, 0x4b, 0xdd, 0x33, 0x98, 0x96, 0xca, 0x89, 0x38, 0x61, 0xab, 0x07, 0x6f, 0xee, 0x5e, 0x01, - 0xcb, 0x09, 0x60, 0x77, 0xc2, 0x9c, 0xc6, 0x50, 0xd5, 0x06, 0xfe, 0x71, 0xf5, 0x60, 0xab, 0xf6, 0xf0, 0x8f, 0xfd, - 0x1f, 0x83, 0x1b, 0x1b, 0x1b, 0xdb, 0x78, 0xbb, 0x96, 0x08, 0xf2, 0x0a, 0x0f, 0xf4, 0xf1, 0xea, 0xa3, 0xa0, 0xe5, - 0x2a, 0xb1, 0xdd, 0x77, 0x28, 0x6c, 0x0d, 0xf2, 0x4d, 0xca, 0xa4, 0xe1, 0xbc, 0xe0, 0xd9, 0x54, 0xce, 0x50, 0xc8, - 0x6b, 0x3e, 0x0e, 0xda, 0x8e, 0xf0, 0x37, 0x70, 0x6a, 0xc7, 0xcb, 0x8b, 0x4f, 0xd0, 0x07, 0x3c, 0x5d, 0x29, 0x4d, - 0x45, 0x9c, 0x52, 0x6e, 0xd1, 0xe5, 0x3a, 0x0f, 0x46, 0x8a, 0x8b, 0x09, 0x2a, 0x1d, 0x77, 0x71, 0xe3, 0x6c, 0xe4, - 0xf4, 0x97, 0x78, 0x75, 0x91, 0x2e, 0x1f, 0x89, 0x6c, 0xd5, 0xd2, 0xfb, 0x5d, 0x9f, 0x6e, 0xdb, 0x53, 0xc6, 0x27, - 0xd9, 0x88, 0x0e, 0x66, 0x7c, 0x9c, 0x08, 0xaf, 0x4f, 0x8c, 0xf4, 0xdd, 0x22, 0x30, 0xdd, 0x1c, 0x9b, 0xfc, 0x70, - 0xbc, 0xde, 0x6c, 0xd6, 0xb8, 0xfd, 0x37, 0xce, 0x13, 0x67, 0x51, 0x62, 0x44, 0x65, 0xa1, 0xe1, 0x01, 0xad, 0x10, - 0x37, 0xef, 0x99, 0xc0, 0xb8, 0xec, 0x92, 0xa4, 0xb6, 0x1b, 0x08, 0x5c, 0xec, 0x71, 0xcc, 0x92, 0x91, 0xed, 0x41, - 0x79, 0xa0, 0x2f, 0x46, 0xd3, 0x2d, 0x60, 0x5a, 0x5e, 0x3b, 0x3b, 0x4f, 0x6d, 0xaf, 0x9a, 0x2a, 0x80, 0x59, 0xb2, - 0x3c, 0x3e, 0x45, 0xd6, 0xfd, 0x16, 0xba, 0x88, 0x01, 0x63, 0xe3, 0xca, 0x9c, 0xbb, 0x58, 0xb5, 0x22, 0xbe, 0xd1, - 0x44, 0x9a, 0xd4, 0x47, 0xd4, 0x77, 0x10, 0xd6, 0xea, 0x2a, 0x07, 0x09, 0xdc, 0x23, 0xef, 0x8e, 0xb8, 0xf4, 0xf4, - 0x99, 0xc5, 0xb8, 0x4a, 0xdf, 0x52, 0xd7, 0xe2, 0x9a, 0x61, 0xaf, 0xb8, 0x0f, 0xf6, 0x07, 0xc6, 0x2d, 0x62, 0x11, - 0x6f, 0xe7, 0xb5, 0x14, 0xd6, 0xc6, 0x1c, 0x68, 0x6e, 0xb8, 0xc1, 0xef, 0xac, 0x5a, 0x33, 0x30, 0xc3, 0x8c, 0x33, - 0x92, 0xdf, 0x8d, 0x7b, 0x55, 0x63, 0x47, 0xae, 0x02, 0x88, 0xbe, 0x05, 0x5d, 0x92, 0xc3, 0x2b, 0x59, 0xae, 0x3a, - 0x43, 0xfe, 0x0d, 0xd6, 0x59, 0x2f, 0x4e, 0xc0, 0x4c, 0x9a, 0xf2, 0x12, 0x13, 0x53, 0xc4, 0xe5, 0x66, 0x19, 0xf3, - 0x34, 0x7d, 0x16, 0xed, 0xe0, 0xe4, 0x46, 0x02, 0x47, 0xec, 0x1b, 0xcb, 0xd0, 0x4c, 0xd8, 0x88, 0x89, 0x34, 0x2a, - 0xa5, 0x84, 0xf7, 0xe5, 0x52, 0x4b, 0xfe, 0x32, 0x97, 0x57, 0x5f, 0x6e, 0x13, 0x1c, 0x90, 0xd7, 0xc0, 0x72, 0x68, - 0x1c, 0xb7, 0x0c, 0x24, 0x62, 0x31, 0x20, 0x46, 0xad, 0xca, 0xe5, 0x64, 0x54, 0x27, 0xf3, 0x15, 0x72, 0xa1, 0x22, - 0x0f, 0x6e, 0x09, 0x94, 0xfc, 0x39, 0xa6, 0x0e, 0x66, 0xa5, 0x76, 0xd3, 0x62, 0x93, 0xe4, 0x3d, 0x33, 0x20, 0xb9, - 0xfa, 0x1a, 0x1e, 0x1a, 0xbf, 0x78, 0x65, 0x4e, 0x09, 0x5f, 0x94, 0xb1, 0xb4, 0x34, 0xe6, 0xd2, 0xbf, 0x90, 0xf7, - 0x69, 0x25, 0x60, 0xbf, 0x81, 0x98, 0x32, 0x70, 0x89, 0x8d, 0x0b, 0x92, 0xf2, 0x5a, 0x9e, 0xb2, 0xfb, 0x1a, 0xca, - 0x77, 0xc9, 0xa4, 0xab, 0x54, 0xd6, 0x35, 0x56, 0xdd, 0x6f, 0x73, 0x96, 0x5f, 0x1e, 0x30, 0xcc, 0x4d, 0x46, 0x83, - 0x6c, 0xc9, 0xcc, 0xa6, 0xfc, 0x6a, 0xef, 0xc6, 0xaf, 0x3c, 0x94, 0x74, 0xa8, 0x56, 0xe9, 0xe6, 0xa5, 0x1b, 0x8e, - 0x71, 0xe3, 0x86, 0x23, 0x80, 0x8d, 0x61, 0xa7, 0x8a, 0xd4, 0x3a, 0xff, 0x7d, 0x39, 0xfc, 0x44, 0x7b, 0x6d, 0xa0, - 0x77, 0xdd, 0x60, 0x65, 0x7a, 0xfa, 0x0d, 0xa8, 0x1a, 0x59, 0x42, 0x37, 0xa1, 0x8a, 0xc9, 0x48, 0x94, 0x98, 0xae, - 0x52, 0x1e, 0xf5, 0x35, 0xe2, 0x1c, 0xc4, 0x0d, 0xe5, 0x2f, 0xfe, 0x35, 0xbc, 0x3c, 0x0e, 0xd0, 0x88, 0x5a, 0x8c, - 0xb3, 0x94, 0xb7, 0xc6, 0xd1, 0x34, 0x4e, 0x2e, 0x83, 0x79, 0xdc, 0x9a, 0x66, 0x69, 0x56, 0xcc, 0x80, 0x2b, 0xbd, - 0xe2, 0x12, 0x6c, 0xf8, 0x69, 0x6b, 0x1e, 0x7b, 0xcf, 0x59, 0x72, 0xc6, 0x78, 0x3c, 0x8c, 0x3c, 0x7b, 0x3f, 0x07, - 0xf1, 0x60, 0xbd, 0x8e, 0xf2, 0x3c, 0x3b, 0xb7, 0xbd, 0x77, 0xd9, 0x09, 0x30, 0xad, 0xf7, 0xe6, 0xe2, 0xf2, 0x94, - 0xa5, 0xde, 0xfb, 0x93, 0x79, 0xca, 0xe7, 0x5e, 0x11, 0xa5, 0x45, 0xab, 0x60, 0x79, 0x3c, 0x06, 0x35, 0x91, 0x64, - 0x79, 0x0b, 0xf3, 0x9f, 0xa7, 0x2c, 0x48, 0xe2, 0xd3, 0x09, 0xb7, 0x46, 0x51, 0xfe, 0xa9, 0xd7, 0x6a, 0xcd, 0xf2, - 0x78, 0x1a, 0xe5, 0x97, 0x2d, 0x6a, 0x11, 0x7c, 0xd1, 0xde, 0x8e, 0xbe, 0x1a, 0xdf, 0xef, 0xf1, 0x1c, 0xfa, 0xc6, - 0x48, 0xc5, 0x00, 0x84, 0x8f, 0xb5, 0xbd, 0xd3, 0x9e, 0x16, 0x6b, 0xe2, 0x44, 0x29, 0x4a, 0x79, 0x79, 0xec, 0x7d, - 0x64, 0x00, 0xb7, 0x7f, 0xc2, 0x53, 0x0f, 0x7c, 0x39, 0x9e, 0xa5, 0x8b, 0xe1, 0x3c, 0x2f, 0x60, 0x80, 0x59, 0x16, - 0xa7, 0x9c, 0xe5, 0xbd, 0x93, 0x2c, 0x07, 0xb2, 0xb5, 0xf2, 0x68, 0x14, 0xcf, 0x8b, 0xe0, 0xfe, 0xec, 0xa2, 0x87, - 0xb6, 0xc2, 0x69, 0x9e, 0xcd, 0xd3, 0x91, 0x9c, 0x2b, 0x4e, 0x61, 0x63, 0xc4, 0xdc, 0xac, 0xa0, 0x2f, 0xa1, 0x00, - 0x7c, 0x29, 0x8b, 0xf2, 0xd6, 0x29, 0x76, 0x46, 0x43, 0xbf, 0x3d, 0x62, 0xa7, 0x5e, 0x7e, 0x7a, 0x12, 0x39, 0x9d, - 0xee, 0x43, 0x4f, 0xfd, 0xf3, 0x77, 0x5c, 0x30, 0xdc, 0x57, 0x16, 0x77, 0xda, 0xed, 0x7f, 0x70, 0x7b, 0x8d, 0x59, - 0x08, 0xa0, 0xa0, 0x33, 0xbb, 0xb0, 0x8a, 0x2c, 0x81, 0xf5, 0x59, 0xd5, 0xb3, 0x37, 0x03, 0xbf, 0x29, 0x4e, 0x4f, - 0x83, 0xee, 0xec, 0xa2, 0x44, 0xec, 0x02, 0x91, 0x90, 0x29, 0x91, 0x94, 0x6f, 0x8b, 0x3f, 0x0a, 0xf1, 0xa3, 0xd5, - 0x10, 0x77, 0x15, 0xc4, 0x15, 0xd5, 0x5b, 0x23, 0xd8, 0x07, 0x44, 0xfe, 0x4e, 0x21, 0x00, 0x99, 0x80, 0x13, 0x98, - 0x2b, 0x38, 0xe8, 0xe5, 0x0f, 0x83, 0xd1, 0x5d, 0x0d, 0xc6, 0xa3, 0xdb, 0xc0, 0xc8, 0xd3, 0xd1, 0xa2, 0xbe, 0xae, - 0x1d, 0x70, 0x4e, 0x7b, 0x13, 0x86, 0xfc, 0x14, 0x74, 0xf1, 0xf9, 0x3c, 0x1e, 0xf1, 0x89, 0x78, 0x24, 0x76, 0x3e, - 0x17, 0x75, 0x3b, 0xed, 0xb6, 0x78, 0x2f, 0x40, 0xa1, 0x05, 0x1d, 0x1f, 0x1b, 0x00, 0x13, 0x7d, 0xb8, 0xee, 0x23, - 0x36, 0x5f, 0xdf, 0xfa, 0xa5, 0x1a, 0xef, 0x5c, 0xe5, 0x0d, 0x0a, 0x15, 0xa1, 0xbe, 0xd9, 0x82, 0x19, 0x6f, 0xf9, - 0x5e, 0x47, 0x1f, 0x54, 0xf5, 0xbf, 0x66, 0xa4, 0xf5, 0x02, 0xee, 0x99, 0xb9, 0x40, 0xbd, 0x74, 0x0f, 0x43, 0x52, - 0xad, 0x96, 0x0b, 0x7a, 0x83, 0x61, 0x08, 0x89, 0x0e, 0x04, 0x9d, 0xbc, 0x5f, 0xd0, 0x37, 0x35, 0x32, 0x37, 0x28, - 0x9c, 0xcc, 0x85, 0x2d, 0x9f, 0x69, 0xb9, 0x0e, 0x4a, 0x1a, 0xbc, 0xec, 0x0f, 0x4c, 0x36, 0x00, 0xe9, 0x4d, 0xa1, - 0xae, 0x1f, 0x42, 0xe1, 0x4a, 0x29, 0x47, 0x6a, 0x7a, 0xd3, 0x15, 0x7d, 0x58, 0x95, 0x98, 0x32, 0x92, 0x8f, 0x06, - 0xff, 0x0a, 0xc3, 0xde, 0xd1, 0x8e, 0x65, 0x91, 0xcd, 0xf3, 0x21, 0x45, 0xea, 0x96, 0x3d, 0x7e, 0x9b, 0x14, 0xae, - 0xed, 0x31, 0x2d, 0xe7, 0xd1, 0x0d, 0xae, 0x7d, 0xe4, 0x80, 0xd3, 0x21, 0x88, 0xb8, 0x63, 0x20, 0xa3, 0x1c, 0x0a, - 0x42, 0x54, 0x5d, 0x23, 0xca, 0x77, 0xa3, 0xfb, 0x97, 0xfe, 0x3c, 0x8d, 0x41, 0xd2, 0x7d, 0x8c, 0x47, 0x74, 0xef, - 0x24, 0x1e, 0xd1, 0x41, 0x44, 0x8b, 0x12, 0x8f, 0x30, 0xb2, 0x0d, 0x05, 0xea, 0x3b, 0x2c, 0xf0, 0x2c, 0x13, 0x59, - 0xec, 0x96, 0x8d, 0x87, 0x09, 0x86, 0xaa, 0x1c, 0x65, 0xd3, 0x28, 0x4e, 0x03, 0xfc, 0x3e, 0x88, 0xa7, 0x47, 0x0c, - 0xb0, 0x8b, 0x07, 0x3f, 0x99, 0xcc, 0x45, 0xeb, 0xb8, 0xfe, 0xaf, 0x20, 0x47, 0xa8, 0x7f, 0x29, 0xfd, 0x30, 0x0d, - 0x97, 0x3a, 0xe6, 0xad, 0x97, 0x82, 0xec, 0xe1, 0xca, 0x66, 0x65, 0x14, 0xe7, 0xd8, 0xe5, 0xf4, 0xa3, 0xdf, 0xea, - 0x04, 0x1d, 0xed, 0xba, 0xd6, 0x6e, 0xa3, 0x8a, 0x5c, 0x16, 0x79, 0xa3, 0x91, 0x60, 0xd0, 0xcf, 0x02, 0xce, 0xea, - 0x5d, 0xc3, 0xea, 0x49, 0xbe, 0xc4, 0x00, 0xce, 0x69, 0xea, 0xd4, 0x80, 0xa0, 0xb3, 0x80, 0x6b, 0xa6, 0x72, 0xcb, - 0x88, 0x94, 0xd2, 0x63, 0xda, 0xc0, 0xf5, 0xbb, 0x44, 0x78, 0x6f, 0xa8, 0x9e, 0x02, 0xa5, 0x58, 0x6e, 0x74, 0xbc, - 0x2b, 0x76, 0xbc, 0x45, 0x3c, 0x16, 0xda, 0xb0, 0x05, 0x6d, 0xeb, 0x2f, 0x43, 0xa0, 0xd2, 0xa7, 0xd0, 0x5e, 0x5f, - 0x38, 0x2a, 0xb1, 0x3e, 0x87, 0xb9, 0xf6, 0x85, 0xd6, 0xa3, 0x1b, 0xf9, 0xf6, 0xde, 0xfa, 0x82, 0x97, 0xbb, 0x5b, - 0xa2, 0xf7, 0xde, 0x71, 0x59, 0x90, 0x82, 0x32, 0x03, 0x69, 0xd5, 0x14, 0xa2, 0x0e, 0x86, 0xa5, 0xf4, 0x5d, 0x1c, - 0x37, 0xd7, 0x46, 0x97, 0x88, 0x18, 0x4b, 0xb6, 0x2b, 0x30, 0x5d, 0x29, 0xca, 0x61, 0x4f, 0xea, 0x84, 0x94, 0x42, - 0xe4, 0x60, 0xf4, 0x96, 0xa1, 0x38, 0x46, 0x08, 0xfa, 0xeb, 0x8b, 0xb8, 0x0c, 0xd7, 0x17, 0x59, 0x79, 0x0c, 0x96, - 0x09, 0x42, 0x95, 0xab, 0xcf, 0xbb, 0xc0, 0xc4, 0x22, 0xc8, 0x62, 0xd1, 0x08, 0x38, 0x2d, 0x2b, 0x6d, 0xab, 0x2f, - 0xa0, 0x01, 0x0f, 0x10, 0x0b, 0xc0, 0x76, 0xa3, 0x5e, 0x0c, 0x70, 0x11, 0xad, 0xf7, 0x60, 0xa0, 0xdd, 0x2d, 0xd1, - 0x08, 0xf0, 0xca, 0x11, 0xe4, 0x0a, 0x2d, 0x4c, 0xc7, 0x31, 0x51, 0x1b, 0xc5, 0x67, 0x9a, 0x74, 0x94, 0x9b, 0xbc, - 0xb7, 0x9b, 0x44, 0x27, 0x2c, 0x81, 0x21, 0x8b, 0xab, 0xab, 0x36, 0x8c, 0x24, 0x5e, 0xad, 0xdd, 0x38, 0x9d, 0xcd, - 0xe5, 0x67, 0xb6, 0x60, 0xe2, 0x0e, 0x9e, 0x7c, 0xe2, 0x2d, 0x80, 0xbe, 0x3a, 0xca, 0x0b, 0xe4, 0x00, 0x00, 0x22, - 0x9d, 0x22, 0x20, 0x74, 0x15, 0x5b, 0x40, 0x69, 0x3c, 0x5a, 0x2e, 0xd3, 0x98, 0xe5, 0xf0, 0x02, 0xdb, 0x72, 0x86, - 0xcf, 0x29, 0x3c, 0x4f, 0xe3, 0x14, 0x1f, 0x0b, 0x7c, 0x8c, 0x2e, 0xf0, 0x31, 0x2b, 0xed, 0xbf, 0xcb, 0x0a, 0x58, - 0x9a, 0x00, 0xd9, 0xd5, 0x15, 0xc8, 0x7b, 0x4d, 0x82, 0xdd, 0x2d, 0x20, 0x16, 0x72, 0x8a, 0xf8, 0xe8, 0x0a, 0x33, - 0xc9, 0xc8, 0x8a, 0x59, 0x4b, 0x94, 0x5b, 0xa4, 0x55, 0x43, 0x70, 0xba, 0x72, 0xa7, 0x61, 0x3c, 0x78, 0x32, 0xbd, - 0xe4, 0x09, 0xbe, 0xb8, 0xb6, 0x25, 0xbe, 0x8a, 0x21, 0x88, 0x42, 0x8f, 0x88, 0xa1, 0x2e, 0xe3, 0xf2, 0x7b, 0x37, - 0x71, 0x68, 0xe3, 0x2c, 0x60, 0xbf, 0xa1, 0x16, 0xe0, 0x51, 0x9c, 0x88, 0xc6, 0x2b, 0xf0, 0x69, 0xe4, 0x09, 0x12, - 0x3a, 0xbb, 0x5b, 0x15, 0x6c, 0x00, 0xfc, 0x50, 0xdc, 0xb2, 0x76, 0x44, 0x0e, 0xa4, 0x2d, 0xca, 0xe9, 0xec, 0x5c, - 0x6e, 0x69, 0x19, 0xd9, 0x15, 0xb1, 0x72, 0x8d, 0x2a, 0xe5, 0x2c, 0xda, 0xe3, 0x28, 0x5d, 0xd5, 0x14, 0xa0, 0x9f, - 0x31, 0x36, 0xf2, 0x6c, 0x0b, 0x64, 0xa9, 0x78, 0xfe, 0x98, 0xb0, 0x33, 0x26, 0xbf, 0x94, 0xa2, 0x07, 0xd1, 0x95, - 0x43, 0x50, 0xc9, 0x5c, 0x5e, 0xe2, 0x94, 0xec, 0xa9, 0x70, 0x94, 0x94, 0xa8, 0x23, 0xe2, 0xd9, 0xc6, 0xa0, 0xcd, - 0x39, 0xda, 0xf5, 0x61, 0xbd, 0x0e, 0x58, 0xbb, 0xb6, 0x80, 0x97, 0xec, 0xb8, 0xdb, 0x91, 0x83, 0x01, 0xd8, 0x78, - 0x0c, 0xdb, 0x45, 0x45, 0x96, 0xb5, 0x2c, 0x10, 0x50, 0x81, 0x53, 0xea, 0xd9, 0xa2, 0x85, 0x5d, 0xb5, 0xd5, 0x4f, - 0x92, 0x38, 0x49, 0x36, 0xfc, 0xb4, 0x62, 0x2e, 0xe0, 0x8e, 0x75, 0x11, 0x69, 0x6d, 0xc8, 0x37, 0xfb, 0x5f, 0xfe, - 0xf9, 0xbf, 0xff, 0x67, 0x18, 0x98, 0xfa, 0xb9, 0xa5, 0x75, 0x7d, 0xab, 0xff, 0x06, 0xad, 0xe6, 0xe9, 0x2d, 0xed, - 0xfe, 0xf6, 0x1f, 0xfe, 0x0b, 0x34, 0xa3, 0x1b, 0x39, 0x6e, 0x79, 0x4c, 0x10, 0x0d, 0xd1, 0x08, 0xfa, 0x2c, 0x90, - 0x6a, 0x83, 0x5c, 0x3b, 0xd3, 0x3f, 0x21, 0xd8, 0x05, 0xcf, 0x66, 0x37, 0x82, 0x83, 0x50, 0x0f, 0x93, 0xac, 0x60, - 0x1a, 0x1e, 0x21, 0x6b, 0x3f, 0x0f, 0x20, 0x9a, 0x6b, 0x06, 0x5c, 0x5e, 0x58, 0x7a, 0x1c, 0xb1, 0x3c, 0xab, 0xc6, - 0x69, 0xac, 0x5e, 0xc1, 0x38, 0xa1, 0x43, 0x71, 0x05, 0x58, 0x2f, 0xf1, 0x04, 0x0f, 0x24, 0x10, 0xdc, 0xfa, 0x57, - 0xbe, 0x56, 0x0f, 0xa6, 0xf9, 0x53, 0x8c, 0x25, 0x42, 0x29, 0x6a, 0x04, 0xf8, 0x09, 0x72, 0xea, 0xa3, 0x7e, 0x8e, - 0x2e, 0xf4, 0x33, 0xca, 0x32, 0x31, 0x01, 0xe8, 0xa2, 0x29, 0x9a, 0x19, 0xe6, 0x0c, 0x22, 0x0d, 0xa0, 0xf2, 0x23, - 0x8d, 0x6c, 0x12, 0x21, 0xbc, 0x3e, 0x62, 0xd2, 0x25, 0x5e, 0xb1, 0x99, 0xe7, 0xec, 0x63, 0x92, 0x9d, 0x63, 0x70, - 0x0a, 0x89, 0x74, 0x5d, 0x7d, 0x69, 0xf9, 0xbe, 0x75, 0x4d, 0xf5, 0x04, 0x76, 0x01, 0xd8, 0x92, 0x3c, 0xd4, 0x54, - 0x6e, 0x20, 0xb5, 0x7a, 0x48, 0xc4, 0x72, 0x75, 0x8d, 0x44, 0x1b, 0x4b, 0x25, 0xd6, 0xf2, 0x11, 0x91, 0xf6, 0xda, - 0x15, 0x53, 0xdc, 0x3e, 0x0f, 0xc2, 0x76, 0xcd, 0x44, 0x58, 0x75, 0xeb, 0x4c, 0xc7, 0x6e, 0x70, 0x03, 0x65, 0x3e, - 0x13, 0xeb, 0xd5, 0x80, 0xdc, 0x01, 0x06, 0x0f, 0x34, 0x80, 0xd0, 0x11, 0x4f, 0x84, 0x84, 0x0f, 0x68, 0x2d, 0xa4, - 0xb8, 0xaf, 0x41, 0xe4, 0x59, 0xff, 0xe7, 0x7f, 0x1e, 0xd2, 0x1c, 0xd8, 0x26, 0xae, 0xf4, 0x2b, 0x99, 0x23, 0x27, - 0xb9, 0x52, 0xbf, 0xaf, 0xf0, 0x82, 0x1d, 0x8d, 0x82, 0x4a, 0xb6, 0x20, 0x9b, 0x80, 0x93, 0x4d, 0x60, 0xea, 0x61, - 0xf4, 0x32, 0xb2, 0xd5, 0xfa, 0xf6, 0x23, 0xf6, 0xab, 0x69, 0x4c, 0x2e, 0x35, 0x66, 0x2c, 0xad, 0x59, 0xa9, 0x31, - 0xf1, 0xfb, 0x29, 0x0d, 0x8d, 0x19, 0x5f, 0xab, 0x31, 0x91, 0x76, 0x7d, 0x1c, 0x39, 0xb4, 0x37, 0x31, 0x94, 0x62, - 0x68, 0x70, 0x8e, 0xb6, 0x09, 0xf8, 0xcf, 0xc3, 0x4f, 0xd2, 0x34, 0x21, 0xc8, 0x31, 0x96, 0x20, 0x8d, 0x2d, 0xb2, - 0x4b, 0x04, 0x76, 0xc7, 0xa5, 0xde, 0xf8, 0xf8, 0x68, 0x4c, 0xc0, 0xdd, 0xc5, 0x98, 0xa1, 0x58, 0x3e, 0xde, 0xc2, - 0x21, 0xc4, 0x7e, 0x29, 0xe9, 0x19, 0x90, 0xda, 0xa2, 0x71, 0xbc, 0x85, 0x08, 0x4a, 0x05, 0xf6, 0xdb, 0x37, 0x07, - 0x87, 0xb6, 0x77, 0x92, 0x8d, 0x2e, 0x03, 0x1b, 0x1c, 0x0a, 0x30, 0x3e, 0x5c, 0x9f, 0x4f, 0x58, 0xea, 0x28, 0x93, - 0x3e, 0x4b, 0xc0, 0xa5, 0xc9, 0x4e, 0xc5, 0x37, 0x14, 0x9a, 0x01, 0x75, 0x20, 0x26, 0x7d, 0x64, 0x71, 0x6f, 0x97, - 0x8b, 0xef, 0x8f, 0xf2, 0x1c, 0x1f, 0xf7, 0x30, 0xa5, 0x60, 0x77, 0x0b, 0x1e, 0xf0, 0xe5, 0x00, 0xd5, 0x91, 0x7e, - 0x13, 0x70, 0x16, 0xe2, 0x7d, 0x0b, 0xdb, 0x6f, 0xa9, 0xbe, 0x08, 0xc5, 0x9e, 0x64, 0x35, 0x6d, 0x75, 0x57, 0xa6, - 0x1c, 0x8d, 0x3d, 0x42, 0x4b, 0x8d, 0xac, 0x6e, 0x20, 0x05, 0x1f, 0xe9, 0x12, 0xa1, 0xfd, 0x8d, 0x22, 0x1a, 0xa5, - 0xd2, 0xd5, 0xb2, 0x0a, 0x27, 0x24, 0x2a, 0x8a, 0xc9, 0xe0, 0x27, 0x81, 0x7f, 0x6c, 0x7e, 0x2f, 0x4c, 0x7c, 0xda, - 0x47, 0x23, 0x79, 0xf4, 0x57, 0xef, 0x23, 0xf3, 0x2e, 0x8f, 0xa9, 0xa5, 0x72, 0x4e, 0x31, 0x6a, 0x82, 0x4e, 0x7c, - 0x5b, 0x45, 0x21, 0xc0, 0x3c, 0x49, 0xa2, 0x59, 0xc1, 0x02, 0xf5, 0x20, 0xfd, 0x54, 0x74, 0x77, 0x97, 0x03, 0x03, - 0xa6, 0x19, 0x53, 0xf2, 0xc9, 0xc2, 0x74, 0x64, 0x1f, 0x80, 0x23, 0x8b, 0x49, 0xf8, 0xad, 0x08, 0x94, 0x6f, 0x1a, - 0x24, 0x6c, 0xcc, 0x4b, 0x8e, 0xb7, 0xbc, 0x17, 0x2a, 0x72, 0xe0, 0x77, 0x77, 0xc0, 0xb9, 0xb5, 0x7c, 0xfc, 0xff, - 0xb6, 0xb1, 0x47, 0x41, 0x0a, 0xce, 0x28, 0x5d, 0xfb, 0xc0, 0x2b, 0x75, 0x00, 0x91, 0xf9, 0xbe, 0x30, 0x26, 0x1a, - 0x32, 0x8c, 0xaa, 0x94, 0x3c, 0x07, 0xb1, 0xed, 0xf1, 0xdc, 0x6c, 0x07, 0xe2, 0x76, 0x29, 0xb4, 0xb2, 0xec, 0xdc, - 0x6f, 0xbb, 0xd2, 0x05, 0x58, 0x6e, 0xac, 0x22, 0x24, 0xf5, 0xb7, 0x25, 0x0a, 0x19, 0xb1, 0x9c, 0x52, 0x68, 0x9a, - 0x85, 0xe8, 0x61, 0xe2, 0xb4, 0x1c, 0xe5, 0xb9, 0xd5, 0x50, 0x2c, 0x69, 0xc7, 0x1f, 0xd1, 0x8e, 0x27, 0x19, 0x36, - 0x58, 0x88, 0xb9, 0x87, 0x51, 0x32, 0x74, 0x10, 0x00, 0xab, 0x65, 0x3d, 0x02, 0x6a, 0xba, 0x2a, 0xd2, 0xe0, 0x3f, - 0x44, 0xe2, 0x96, 0x42, 0xe2, 0xad, 0xa0, 0xd2, 0xf1, 0xa0, 0x2c, 0x7b, 0xe7, 0xcc, 0x39, 0xfa, 0x5d, 0x5e, 0x1a, - 0x10, 0x77, 0x45, 0xf5, 0xf7, 0xf6, 0xda, 0xa5, 0x3b, 0xf0, 0x7e, 0x30, 0x3e, 0x62, 0x66, 0x2b, 0x86, 0xb6, 0x3d, - 0x58, 0x86, 0x1f, 0x42, 0xec, 0xfb, 0xca, 0xb1, 0xd1, 0xb2, 0xa4, 0x9a, 0xcb, 0x16, 0xf1, 0x97, 0x8d, 0xdd, 0x44, - 0xc8, 0xfb, 0xfb, 0xeb, 0x22, 0x17, 0xdf, 0xdc, 0x1e, 0xb9, 0x60, 0x77, 0x8c, 0x5c, 0x7c, 0xf3, 0x27, 0x47, 0x2e, - 0xbe, 0x6f, 0x46, 0x2e, 0x7e, 0xfb, 0x9c, 0xc8, 0x45, 0x9e, 0x9d, 0x17, 0x61, 0x47, 0x9e, 0x94, 0x83, 0xcc, 0xf9, - 0xfb, 0x84, 0x30, 0x60, 0xa2, 0x46, 0x00, 0x83, 0x22, 0x16, 0x22, 0xb9, 0x0f, 0x24, 0xbb, 0x8c, 0x17, 0xb4, 0x75, - 0x16, 0x5d, 0xeb, 0xbe, 0xba, 0x36, 0x04, 0x1e, 0x9b, 0xab, 0x2f, 0xbc, 0x75, 0x55, 0x44, 0x21, 0xa0, 0xef, 0x7e, - 0xea, 0x8e, 0xdd, 0x4d, 0x95, 0xbe, 0x65, 0x8e, 0xd0, 0x53, 0x51, 0x79, 0xc1, 0x3e, 0x0b, 0xfb, 0xdf, 0x1d, 0x75, - 0x7a, 0xdb, 0x9d, 0x29, 0xf4, 0x06, 0x2d, 0x0a, 0x6f, 0xed, 0xde, 0xf6, 0x36, 0xbe, 0x9d, 0xab, 0xb7, 0x2e, 0xbe, - 0xc5, 0xea, 0x6d, 0x07, 0xdf, 0x86, 0xea, 0xed, 0x01, 0xbe, 0x8d, 0xd4, 0xdb, 0x43, 0x7c, 0x3b, 0xb3, 0xcb, 0x23, - 0xae, 0x81, 0x7b, 0x08, 0x7c, 0x45, 0xc6, 0x7e, 0xa0, 0xca, 0x60, 0xd3, 0xe2, 0x75, 0xbb, 0xe8, 0x34, 0x88, 0x3d, - 0xe1, 0x14, 0x05, 0xb9, 0x77, 0x0e, 0x92, 0x3f, 0xa0, 0xec, 0xb2, 0xa7, 0xf8, 0xdd, 0x05, 0xf0, 0x21, 0x0e, 0xe3, - 0x29, 0x53, 0x1f, 0xa0, 0x55, 0x58, 0x83, 0x1d, 0x79, 0xd4, 0x1e, 0x94, 0x3d, 0xbd, 0x4e, 0x22, 0x60, 0xa2, 0x4e, - 0xef, 0x69, 0xe5, 0xaa, 0x3a, 0x31, 0x5d, 0x4b, 0xaf, 0xf0, 0x35, 0x7a, 0xc4, 0x70, 0xa3, 0xc7, 0x60, 0x21, 0xb5, - 0x2e, 0xc0, 0xf1, 0x5a, 0xa9, 0x5b, 0x10, 0x22, 0xad, 0x4d, 0x08, 0x27, 0xfd, 0x76, 0x18, 0x9d, 0xea, 0xe7, 0x57, - 0x60, 0xf0, 0x46, 0xa7, 0xec, 0x36, 0x3d, 0x43, 0x20, 0x9a, 0x3a, 0x46, 0x01, 0x41, 0xf6, 0x10, 0x2c, 0x0d, 0x3a, - 0x80, 0x52, 0xc7, 0x20, 0x75, 0xea, 0x5a, 0x87, 0xa6, 0xaf, 0x17, 0x01, 0x45, 0xab, 0x82, 0x5d, 0xb0, 0xbb, 0xa9, - 0x54, 0x50, 0x18, 0x2a, 0xb0, 0xe0, 0x46, 0x55, 0xa4, 0x7d, 0xe4, 0x6b, 0x15, 0x92, 0xa5, 0x74, 0x91, 0x19, 0xcd, - 0xd7, 0xa1, 0xfc, 0x65, 0xf1, 0xf8, 0x45, 0x67, 0x88, 0x7f, 0xa4, 0xf0, 0xfd, 0x62, 0x3c, 0x1e, 0xdf, 0xa8, 0x9b, - 0xbe, 0x18, 0x8d, 0x59, 0x97, 0xed, 0xf4, 0x30, 0xd2, 0xdb, 0x92, 0xe2, 0xb0, 0x53, 0x12, 0xed, 0x16, 0x77, 0x6b, - 0x8c, 0x92, 0x13, 0xd4, 0xd5, 0xdd, 0x95, 0x58, 0x09, 0x54, 0x59, 0x80, 0xf0, 0x3e, 0x8d, 0xd3, 0xa0, 0x5d, 0xfa, - 0x67, 0x52, 0xea, 0x7f, 0xf1, 0xe8, 0xd1, 0xa3, 0xd2, 0x1f, 0xa9, 0xb7, 0xf6, 0x68, 0x54, 0xfa, 0xc3, 0x85, 0x46, - 0xa3, 0xdd, 0x1e, 0x8f, 0x4b, 0x3f, 0x56, 0x05, 0xdb, 0xdd, 0xe1, 0x68, 0xbb, 0x5b, 0xfa, 0xe7, 0x46, 0x8b, 0xd2, - 0x67, 0xf2, 0x2d, 0x67, 0xa3, 0x5a, 0xb8, 0xf8, 0x61, 0x1b, 0x2a, 0x05, 0xa3, 0x2d, 0xd0, 0xc9, 0x13, 0x8f, 0x41, - 0x34, 0xe7, 0x59, 0x79, 0x0c, 0xb2, 0x9d, 0x81, 0x7c, 0x1e, 0x4b, 0xd9, 0x2e, 0xbe, 0xef, 0x8a, 0x12, 0xfd, 0x37, - 0x53, 0xa2, 0x23, 0x33, 0x93, 0x34, 0x67, 0xa4, 0x07, 0x9a, 0xd5, 0xc8, 0x59, 0x54, 0xfd, 0x5b, 0xc8, 0x2a, 0x61, - 0x8f, 0xd2, 0x06, 0x5b, 0x0a, 0x19, 0xff, 0xed, 0x75, 0x32, 0xfe, 0xbb, 0xdb, 0x65, 0xfc, 0xc9, 0xdd, 0x44, 0xfc, - 0x77, 0x7f, 0xb2, 0x88, 0xff, 0xd6, 0x14, 0xf1, 0x42, 0x88, 0x5d, 0x82, 0xf5, 0x4a, 0x66, 0xeb, 0x49, 0x76, 0xd1, - 0xc2, 0x2d, 0x91, 0xdb, 0x24, 0x3d, 0xd7, 0xef, 0x24, 0xfc, 0x57, 0xe4, 0xff, 0xa8, 0xc1, 0x8c, 0x8f, 0xc5, 0xf2, - 0xec, 0xf4, 0x34, 0x61, 0x4a, 0xc6, 0x1b, 0x15, 0x64, 0x0e, 0xbf, 0x49, 0x43, 0xfb, 0x0d, 0x38, 0xa8, 0x46, 0xc9, - 0x78, 0x0c, 0x45, 0xe3, 0xb1, 0xad, 0xf2, 0x63, 0x41, 0x9e, 0x51, 0xab, 0xd7, 0xb5, 0x12, 0x6a, 0xf5, 0xf5, 0xd7, - 0x66, 0x99, 0x59, 0x20, 0x23, 0x51, 0xa6, 0x31, 0x21, 0x6b, 0x46, 0x71, 0x81, 0x7b, 0xb0, 0xfa, 0xb8, 0x2d, 0xda, - 0x2b, 0x53, 0x50, 0x2a, 0xf1, 0x10, 0xbf, 0x9a, 0xd2, 0xfc, 0x90, 0x88, 0xc8, 0x65, 0x5e, 0x46, 0xae, 0x3a, 0xef, - 0x34, 0xbe, 0x59, 0x57, 0x9d, 0x71, 0xc2, 0xe2, 0xcb, 0x7c, 0x86, 0xc7, 0x97, 0x2f, 0x46, 0xce, 0x25, 0xd8, 0xb1, - 0x71, 0xf1, 0x26, 0x6d, 0xe4, 0x89, 0x09, 0xb0, 0xc3, 0xd0, 0xc4, 0xb4, 0x14, 0x04, 0xab, 0x12, 0xe6, 0xab, 0xca, - 0x9e, 0xd1, 0x49, 0xa6, 0x13, 0xe1, 0x90, 0xfd, 0x1a, 0x59, 0x02, 0x73, 0x30, 0xa9, 0x0b, 0xe9, 0xe3, 0xe5, 0x22, - 0xc9, 0xe2, 0x4c, 0x7e, 0xe5, 0x9a, 0xa2, 0xff, 0x0b, 0xa9, 0x3f, 0xe4, 0xf1, 0x7b, 0xd5, 0x13, 0x03, 0xee, 0x62, - 0x86, 0x51, 0xa9, 0x82, 0xec, 0x40, 0xb8, 0x19, 0x7e, 0x92, 0x47, 0x0c, 0xa1, 0x62, 0xd9, 0x15, 0xf5, 0xf0, 0x13, - 0x28, 0xd5, 0x97, 0x21, 0x6b, 0x5f, 0x11, 0x6c, 0xf0, 0x00, 0x7e, 0xdd, 0x9f, 0xa3, 0x36, 0xc8, 0xe6, 0xdc, 0x71, - 0xa8, 0x95, 0xe3, 0x96, 0x5e, 0x77, 0x07, 0x3c, 0x4a, 0xd7, 0x17, 0xdf, 0xfd, 0x71, 0x74, 0x67, 0x89, 0xef, 0x75, - 0xa1, 0xf3, 0xa5, 0xef, 0x70, 0x69, 0x12, 0xe3, 0x87, 0x42, 0x04, 0xa2, 0xc6, 0x5d, 0x11, 0xb5, 0x88, 0xcd, 0x77, - 0x5f, 0xb9, 0x6f, 0x06, 0x61, 0xdd, 0x55, 0x1c, 0x2c, 0xe3, 0x64, 0xf5, 0x42, 0x6c, 0x2b, 0xac, 0x9a, 0x65, 0x70, - 0x6e, 0xd1, 0x99, 0xc5, 0xb9, 0x11, 0x77, 0xae, 0x6d, 0x83, 0x52, 0x05, 0x9e, 0x45, 0xf4, 0xf8, 0x12, 0x63, 0xa4, - 0xc2, 0xf7, 0x55, 0x40, 0xd7, 0xbd, 0x4e, 0x03, 0x72, 0xf4, 0x47, 0x35, 0xa3, 0xab, 0x2a, 0x55, 0x50, 0x9a, 0xa7, - 0x04, 0x06, 0x32, 0x14, 0x00, 0x86, 0x35, 0x4e, 0x85, 0xde, 0x82, 0x69, 0x48, 0x00, 0x6b, 0x8f, 0x0c, 0xdd, 0x12, - 0x5b, 0x81, 0x2d, 0xa4, 0x05, 0x28, 0x3d, 0xec, 0xb0, 0x67, 0xd5, 0x40, 0x4f, 0x97, 0xe3, 0xc6, 0x37, 0x39, 0x69, - 0x97, 0xc7, 0x7e, 0x71, 0xee, 0xc1, 0x3f, 0xeb, 0xcb, 0x05, 0x48, 0xf9, 0x93, 0x4f, 0x31, 0x07, 0x9b, 0x7a, 0xd6, - 0xc2, 0x28, 0x08, 0x85, 0x31, 0xa5, 0x3a, 0xa4, 0xa3, 0x47, 0x71, 0x45, 0xa8, 0x37, 0x2f, 0xd0, 0x97, 0x23, 0xa7, - 0x25, 0x48, 0xb3, 0x94, 0xf5, 0xea, 0x47, 0xcc, 0xa6, 0xdf, 0xa0, 0x88, 0x35, 0x58, 0x64, 0xe8, 0xfb, 0xf1, 0x4b, - 0xf0, 0xfd, 0x84, 0x1a, 0x6d, 0x2b, 0xa7, 0xa1, 0xbd, 0xb2, 0x7d, 0x20, 0x69, 0xbb, 0x49, 0xd6, 0x42, 0xbe, 0xec, - 0x1c, 0x5d, 0xe7, 0xdc, 0xdc, 0x76, 0xe0, 0xda, 0xdd, 0xd9, 0xf1, 0xd4, 0x3f, 0xe3, 0xa4, 0xba, 0x59, 0x4c, 0x07, - 0xae, 0x77, 0x81, 0x2c, 0x88, 0xc6, 0xf8, 0x45, 0xbd, 0xbb, 0xb4, 0x3c, 0xa1, 0x6c, 0xc7, 0x05, 0xaa, 0xf5, 0xa0, - 0xf3, 0x08, 0xbc, 0xb5, 0x3b, 0x0f, 0x7f, 0x33, 0xfa, 0xa5, 0xa4, 0x91, 0xba, 0xb4, 0x6a, 0xdb, 0x3d, 0x94, 0x17, - 0x49, 0x74, 0x09, 0x4e, 0x23, 0xd9, 0x18, 0x27, 0x18, 0xc4, 0xed, 0xcd, 0x32, 0x99, 0x39, 0x90, 0xb3, 0x84, 0x7e, - 0x6d, 0x85, 0x5c, 0x8a, 0xed, 0x07, 0xb3, 0x0b, 0xb5, 0x1a, 0x9d, 0x46, 0x46, 0xc0, 0x9f, 0x7a, 0xf0, 0x7f, 0x7d, - 0xa6, 0x41, 0xfd, 0xf0, 0x7a, 0x07, 0x60, 0x10, 0x86, 0x4d, 0x2b, 0x17, 0x50, 0xb5, 0xa1, 0xc4, 0x48, 0x7d, 0xa8, - 0x06, 0xb2, 0xfc, 0x6d, 0x50, 0x95, 0x51, 0xc1, 0x7a, 0xf8, 0x69, 0xc3, 0x18, 0x5c, 0x53, 0x69, 0x3c, 0x4d, 0xe3, - 0xd1, 0x28, 0x61, 0x3d, 0x65, 0x1f, 0x59, 0x9d, 0x07, 0x98, 0x39, 0x60, 0x2e, 0x59, 0x7d, 0x55, 0x0c, 0xe2, 0x69, - 0x3a, 0x45, 0x27, 0x60, 0xaf, 0xe1, 0xf7, 0x09, 0x57, 0x92, 0x53, 0x1e, 0xa9, 0xb7, 0x2b, 0xe2, 0xd1, 0x73, 0x1d, - 0x97, 0x1d, 0x30, 0x16, 0x69, 0xc1, 0xdb, 0x3d, 0x9e, 0xcd, 0x82, 0xd6, 0x76, 0x1d, 0x11, 0xac, 0xd2, 0x28, 0x78, - 0x2b, 0xd0, 0xf2, 0xd0, 0x3a, 0x10, 0x5a, 0xce, 0xf2, 0x3b, 0xb2, 0x8c, 0x06, 0xc0, 0x6f, 0xe0, 0xe9, 0xa2, 0xb2, - 0x8e, 0xcc, 0xff, 0x9f, 0xdd, 0xf2, 0xd5, 0xea, 0xdd, 0xf2, 0x95, 0xda, 0x2d, 0x37, 0x73, 0xec, 0x17, 0xe3, 0x0e, - 0xfe, 0xe9, 0x55, 0x08, 0xc1, 0xaa, 0x00, 0x39, 0x2c, 0xb4, 0x8b, 0x5b, 0x5d, 0xf8, 0x8f, 0x86, 0x6e, 0x7b, 0xf8, - 0xc7, 0x07, 0x0b, 0xb0, 0x6d, 0x61, 0x21, 0xfe, 0x6b, 0xd7, 0xaa, 0x3a, 0xf7, 0xb1, 0x0e, 0x7b, 0xed, 0x2c, 0xd7, - 0x75, 0x6f, 0xde, 0xb4, 0x20, 0xaf, 0xb8, 0x13, 0x28, 0x61, 0x0c, 0xae, 0x5a, 0x74, 0x72, 0x02, 0xa5, 0xe3, 0x6c, - 0x38, 0x2f, 0xfe, 0x51, 0xc2, 0x2f, 0x89, 0x78, 0xe3, 0x96, 0x6e, 0x8c, 0xa3, 0xba, 0x8a, 0x34, 0x14, 0x35, 0xc2, - 0x42, 0xaf, 0x53, 0x50, 0x00, 0x63, 0x32, 0xa7, 0xeb, 0xdf, 0x5f, 0xb1, 0x09, 0xfe, 0x2e, 0x6b, 0xb3, 0x12, 0x99, - 0x7f, 0x2d, 0x31, 0x6e, 0x24, 0xc2, 0x67, 0xd1, 0xc0, 0x5c, 0xc3, 0xf6, 0xa3, 0xd5, 0xe0, 0x1e, 0xa9, 0x99, 0x06, - 0x4a, 0x29, 0x48, 0xbd, 0x03, 0x5e, 0x40, 0x34, 0x4f, 0xf8, 0xcd, 0xa3, 0x5e, 0xc7, 0x19, 0x4b, 0xa3, 0xde, 0x20, - 0xd0, 0xab, 0xb6, 0x77, 0x94, 0xd2, 0x5f, 0x7c, 0x75, 0x1f, 0xff, 0x88, 0xc0, 0xd7, 0x49, 0xe5, 0x1b, 0x89, 0xd8, - 0x00, 0xfa, 0x46, 0xeb, 0x35, 0xe7, 0x47, 0x68, 0x70, 0xf2, 0x7f, 0xee, 0xda, 0x1a, 0x8d, 0xf5, 0x3b, 0x35, 0x97, - 0x56, 0xe9, 0x67, 0xb5, 0xfe, 0xbc, 0xc1, 0xef, 0xd8, 0x76, 0x28, 0x1c, 0x82, 0x7a, 0x5b, 0xf9, 0x2b, 0x44, 0x96, - 0x1a, 0x2b, 0x8a, 0xdf, 0xb5, 0x7d, 0x65, 0x12, 0x53, 0x8f, 0xf5, 0xf0, 0x44, 0x3b, 0x91, 0xf2, 0xcc, 0x19, 0x7b, - 0x08, 0x3f, 0xf2, 0x27, 0x16, 0xde, 0xc3, 0xaf, 0x6f, 0x59, 0x17, 0xd3, 0x24, 0x05, 0xb3, 0x6a, 0xc2, 0xf9, 0x2c, - 0xd8, 0xda, 0x3a, 0x3f, 0x3f, 0xf7, 0xcf, 0xb7, 0xfd, 0x2c, 0x3f, 0xdd, 0xea, 0xb6, 0xdb, 0x6d, 0xfc, 0x68, 0x92, - 0x6d, 0x9d, 0xc5, 0xec, 0xfc, 0x31, 0xb8, 0x1f, 0xf6, 0x43, 0xeb, 0x91, 0xf5, 0x70, 0xdb, 0xda, 0x79, 0x60, 0x5b, - 0xa4, 0x00, 0xa0, 0x64, 0xdb, 0xb6, 0x84, 0x02, 0x08, 0x6d, 0x28, 0xde, 0xdb, 0x3d, 0x55, 0x36, 0x1c, 0x26, 0xa5, - 0x0b, 0x0b, 0x09, 0xfc, 0xb7, 0xec, 0x13, 0xab, 0x6f, 0x75, 0x51, 0xd6, 0x92, 0x6a, 0x44, 0xbd, 0xe2, 0x7e, 0x1f, - 0x46, 0xb3, 0x80, 0xd8, 0xc8, 0x2c, 0xc4, 0x30, 0x99, 0x28, 0xa5, 0x29, 0xd0, 0x2e, 0x3d, 0x81, 0x27, 0x70, 0x0b, - 0x26, 0x16, 0x3c, 0xbf, 0xea, 0x3e, 0x04, 0x1d, 0x77, 0xd6, 0xba, 0x3f, 0x6c, 0xb7, 0x3a, 0x56, 0xa7, 0xd5, 0xf5, - 0x1f, 0x5a, 0x5d, 0xf1, 0x3f, 0xc8, 0xc8, 0x6d, 0xab, 0x03, 0x4f, 0xdb, 0x16, 0xbc, 0x9f, 0xdd, 0x17, 0x29, 0x11, - 0x91, 0xbd, 0xb5, 0xb7, 0x8b, 0xbf, 0x43, 0x08, 0x90, 0xfa, 0xca, 0x16, 0xbf, 0xf5, 0xcc, 0xfe, 0xc2, 0x2c, 0xed, - 0x3c, 0x5a, 0x59, 0xdc, 0x7d, 0xb8, 0xb2, 0x78, 0xfb, 0xc1, 0xca, 0xe2, 0xfb, 0x3b, 0xf5, 0xe2, 0xad, 0x53, 0x51, - 0xa5, 0xe5, 0x42, 0x68, 0x4f, 0x23, 0x60, 0x94, 0x0b, 0xa7, 0x03, 0x70, 0xb6, 0xad, 0x16, 0xfe, 0x78, 0xd8, 0x75, - 0x75, 0xaf, 0x13, 0xec, 0xa5, 0xb1, 0x7c, 0xf8, 0x08, 0xb0, 0x7c, 0xde, 0x7d, 0x30, 0xc4, 0x76, 0x84, 0x28, 0xfc, - 0x3b, 0xdb, 0x7e, 0x34, 0x04, 0x8d, 0x60, 0xe1, 0x3f, 0xf8, 0x33, 0xd9, 0xe9, 0x0e, 0xc5, 0x4b, 0x1b, 0xeb, 0x3f, - 0x74, 0x1e, 0x16, 0xd0, 0x14, 0xff, 0xfc, 0xae, 0x4d, 0x68, 0x34, 0xe0, 0xcd, 0x71, 0xef, 0x03, 0x8d, 0x1e, 0x4d, - 0xba, 0xfe, 0x57, 0x67, 0x0f, 0xfd, 0x47, 0x93, 0xce, 0xc3, 0x0f, 0xe2, 0x2d, 0x01, 0x0a, 0x7e, 0x85, 0xff, 0x3e, - 0x6c, 0xb7, 0x27, 0xad, 0x8e, 0xff, 0xe8, 0x6c, 0xdb, 0xdf, 0x4e, 0x5a, 0x0f, 0xfc, 0x47, 0xf8, 0xaf, 0x1a, 0x6e, - 0x92, 0x4d, 0x99, 0x6d, 0xe1, 0x7a, 0x37, 0xfc, 0x5e, 0x73, 0x8e, 0xee, 0x7d, 0x6b, 0xe7, 0xfe, 0xf3, 0x47, 0xb0, - 0x46, 0x93, 0x4e, 0x17, 0xfe, 0xbf, 0xee, 0xf1, 0x03, 0x12, 0x5e, 0x0e, 0x1c, 0x31, 0x4c, 0x27, 0x55, 0x84, 0xa3, - 0x0f, 0x78, 0xdd, 0xf3, 0x7e, 0xbc, 0x2e, 0x00, 0xf2, 0xd7, 0xdb, 0x03, 0x20, 0x3f, 0xdc, 0x31, 0xc8, 0xfd, 0xd7, - 0x3f, 0x39, 0x02, 0xf2, 0x63, 0x33, 0xc8, 0xbd, 0xcf, 0x96, 0x02, 0x1d, 0x4d, 0x67, 0xed, 0x39, 0x73, 0x8e, 0x7e, - 0x62, 0x03, 0x4c, 0x9d, 0x86, 0xd6, 0x3f, 0xd4, 0xe2, 0x41, 0x19, 0x6e, 0xe4, 0x7d, 0x26, 0x76, 0x32, 0xe3, 0xd7, - 0x10, 0x84, 0xf3, 0x5b, 0x09, 0xf2, 0xe2, 0x6e, 0xf4, 0xe0, 0xfc, 0xcf, 0xa5, 0x07, 0x7d, 0xbd, 0x5f, 0xd1, 0xa3, - 0x16, 0x71, 0xa7, 0x88, 0x01, 0x39, 0xfa, 0x07, 0xf4, 0xee, 0xd8, 0x5b, 0x0c, 0xdf, 0x0a, 0x5b, 0xe4, 0x03, 0xbe, - 0xfb, 0x9c, 0xd3, 0x01, 0x91, 0x59, 0x1c, 0xda, 0x32, 0x00, 0x33, 0xc3, 0xef, 0xd3, 0xaa, 0x97, 0x33, 0x71, 0x7b, - 0x25, 0xa4, 0xab, 0x67, 0x3b, 0x3a, 0x78, 0x83, 0xc9, 0xde, 0xe1, 0x22, 0xe3, 0x11, 0xfe, 0x02, 0x24, 0x1e, 0xf3, - 0x04, 0x2f, 0xc6, 0xca, 0x4b, 0x64, 0x98, 0x9b, 0xfc, 0x1d, 0xe6, 0x55, 0xab, 0x43, 0x82, 0x29, 0x06, 0x0c, 0x5e, - 0xb1, 0x51, 0x1c, 0x39, 0xb6, 0x33, 0x83, 0x1d, 0x0b, 0x63, 0xb6, 0x6a, 0x49, 0xcd, 0x94, 0xcf, 0xec, 0xda, 0xea, - 0x77, 0xee, 0xe4, 0xf8, 0x0d, 0xb3, 0xf0, 0x48, 0x06, 0x18, 0x6d, 0xe9, 0x01, 0xc0, 0xf8, 0xaa, 0x24, 0x47, 0x61, - 0x5f, 0x59, 0x0d, 0xb6, 0x30, 0x1b, 0x3a, 0x7e, 0x17, 0xdc, 0x08, 0x2a, 0xc6, 0x6f, 0x42, 0xfd, 0xe8, 0xb4, 0xb6, - 0xc1, 0xac, 0x31, 0xba, 0xe9, 0x81, 0x06, 0x4b, 0x61, 0x24, 0x11, 0x1c, 0x68, 0x94, 0x7a, 0xfa, 0x97, 0x90, 0x55, - 0xe1, 0xa2, 0xe2, 0xf1, 0xe5, 0xa1, 0xbc, 0xf7, 0x6d, 0x63, 0xe4, 0x96, 0x22, 0xf6, 0xd5, 0x37, 0xa7, 0x36, 0x41, - 0x5d, 0xd0, 0x6f, 0x82, 0xa4, 0x73, 0x6f, 0xd4, 0x08, 0x98, 0x76, 0x6d, 0x49, 0xcf, 0x21, 0xb4, 0x85, 0x3e, 0x18, - 0xb1, 0xb3, 0x78, 0x28, 0xc5, 0xba, 0x67, 0xc9, 0xeb, 0x22, 0x2d, 0xc2, 0x22, 0xec, 0x78, 0xc2, 0x77, 0x86, 0x17, - 0xd4, 0x6a, 0x61, 0x9a, 0xd9, 0x7b, 0xf7, 0x7a, 0x1a, 0x92, 0x7a, 0xc6, 0xba, 0x8d, 0xbf, 0x96, 0xf2, 0x08, 0x7c, - 0xb5, 0x7f, 0x0c, 0xef, 0xe1, 0x2f, 0xa6, 0xbc, 0x37, 0xb0, 0x5d, 0x9f, 0x84, 0xe2, 0xbd, 0xea, 0xb7, 0x53, 0xa2, - 0x44, 0xd8, 0x04, 0xfd, 0xe5, 0xdd, 0x55, 0x91, 0x49, 0xa5, 0xd5, 0xdd, 0xa9, 0x94, 0x16, 0x3c, 0x1b, 0x52, 0x0a, - 0x04, 0x68, 0xd7, 0xdf, 0x31, 0x44, 0xe1, 0x59, 0x0b, 0x7f, 0xd6, 0x84, 0xe1, 0x7d, 0x68, 0xa0, 0xa4, 0xe1, 0x4b, - 0x68, 0xbe, 0x2d, 0x04, 0x2f, 0xf4, 0xfb, 0x89, 0x44, 0x95, 0x10, 0x53, 0x75, 0x8e, 0x59, 0x71, 0x88, 0x24, 0x72, - 0x04, 0x6c, 0xcf, 0x88, 0x37, 0x09, 0x76, 0x95, 0xd1, 0x94, 0xa7, 0xd0, 0xd7, 0xd1, 0x9f, 0x72, 0x5e, 0x55, 0xe7, - 0xd5, 0x76, 0xce, 0x8a, 0x29, 0x90, 0xe1, 0x1b, 0x07, 0x55, 0x74, 0x7d, 0x41, 0x7c, 0xd2, 0x4c, 0x6c, 0xe3, 0xea, - 0xa3, 0x6f, 0x2b, 0x32, 0x70, 0xcd, 0x4d, 0xc1, 0x2a, 0xa6, 0xa1, 0x7d, 0x81, 0x69, 0x33, 0xf8, 0xb3, 0x2a, 0x56, - 0x0f, 0x92, 0xa1, 0xfc, 0x24, 0xc2, 0xdf, 0xc8, 0x42, 0x3f, 0xca, 0x6a, 0x03, 0x72, 0xfa, 0x66, 0x25, 0x41, 0xfa, - 0x62, 0x54, 0x36, 0x91, 0x00, 0x7b, 0x01, 0x7f, 0xc1, 0x5f, 0x75, 0x5d, 0x42, 0xde, 0x83, 0xc4, 0x9c, 0x82, 0x51, - 0x9c, 0xd3, 0xf5, 0x5a, 0x85, 0x7f, 0x2d, 0xa2, 0x59, 0x91, 0x9a, 0x76, 0x25, 0x2b, 0xfa, 0x36, 0x16, 0xd9, 0x81, - 0x4c, 0x48, 0x33, 0x3f, 0xda, 0x6c, 0xde, 0x7f, 0x1c, 0x89, 0x5c, 0x34, 0xfc, 0xa8, 0xbd, 0x2d, 0x88, 0x6c, 0x83, - 0x18, 0xbb, 0x16, 0x27, 0x32, 0x6e, 0xf0, 0xda, 0x60, 0xf5, 0x5b, 0x8a, 0xcc, 0x0d, 0x6f, 0x9b, 0xab, 0xa5, 0xc7, - 0xa5, 0x75, 0x70, 0x65, 0xfc, 0xee, 0x84, 0x45, 0xdc, 0x8f, 0x52, 0xca, 0x4f, 0x72, 0x0c, 0xb1, 0xe0, 0x75, 0xd8, - 0xb6, 0x5b, 0x82, 0xe4, 0x31, 0x7e, 0x8d, 0x93, 0x20, 0xbd, 0x0f, 0x85, 0x55, 0xd2, 0xd6, 0xee, 0xa4, 0xbb, 0xf7, - 0xe6, 0x70, 0xdf, 0x12, 0xbb, 0x79, 0x77, 0x0b, 0x5e, 0x77, 0xc9, 0x1d, 0x16, 0xf9, 0x19, 0xa1, 0xc8, 0xcf, 0xb0, - 0x44, 0x52, 0x57, 0x68, 0x6f, 0x09, 0x34, 0x6d, 0x8b, 0xa5, 0x43, 0x11, 0xc3, 0x9b, 0x82, 0xbb, 0x10, 0xe3, 0x87, - 0xbd, 0xb6, 0xb0, 0x5b, 0x0b, 0x57, 0x1a, 0xb6, 0x80, 0x08, 0xf8, 0x29, 0x03, 0x81, 0xa7, 0x2a, 0xe2, 0x07, 0x6a, - 0x9d, 0xa9, 0x64, 0x17, 0x39, 0x94, 0xce, 0x4b, 0x5d, 0x6e, 0x5d, 0xcc, 0x4f, 0xa6, 0x20, 0x87, 0x54, 0x82, 0xca, - 0x7b, 0xd9, 0x61, 0x97, 0xa6, 0xa2, 0x0c, 0x94, 0xa5, 0x88, 0xe4, 0xa4, 0xb3, 0xb7, 0x1b, 0x49, 0x7b, 0x07, 0xf7, - 0x6e, 0x01, 0x9b, 0x17, 0xd4, 0x1c, 0x1a, 0x15, 0x7e, 0x9c, 0x6d, 0x9d, 0xb3, 0x93, 0x56, 0x34, 0x8b, 0xab, 0xf0, - 0x1f, 0x6a, 0xbf, 0xbd, 0x5d, 0xa5, 0x08, 0x65, 0xaa, 0xa5, 0x7c, 0x8c, 0x8c, 0x2c, 0x0e, 0x24, 0x1c, 0x31, 0x68, - 0x29, 0x63, 0x8b, 0x64, 0x34, 0x02, 0xf1, 0x01, 0x56, 0xe2, 0x5f, 0x15, 0x83, 0x94, 0x9a, 0xa0, 0xb4, 0xf7, 0xfe, - 0xf6, 0x3f, 0xfe, 0x97, 0x0c, 0x2b, 0x02, 0x59, 0x01, 0x2c, 0x4c, 0x83, 0xa9, 0x4e, 0x18, 0xd9, 0x05, 0x38, 0xa2, - 0xf1, 0xa8, 0x35, 0x89, 0x92, 0x31, 0x40, 0x50, 0x30, 0x71, 0x9d, 0x49, 0xd6, 0x03, 0x17, 0x48, 0xb0, 0xcc, 0xc3, - 0x79, 0x09, 0x5e, 0xbd, 0x08, 0x57, 0xec, 0x0f, 0xe5, 0xae, 0xaa, 0x9c, 0x61, 0x62, 0x68, 0x23, 0x93, 0xd5, 0xe0, - 0xb9, 0x5a, 0x36, 0xab, 0xfa, 0x25, 0x49, 0x52, 0x78, 0xb0, 0x5a, 0x2a, 0x2b, 0xb4, 0xd4, 0x07, 0x21, 0xff, 0xf2, - 0xcf, 0xff, 0xf1, 0xbf, 0xaa, 0x57, 0x3c, 0xdf, 0xf8, 0xdb, 0x3f, 0xfd, 0xfb, 0xff, 0xfb, 0xbf, 0xff, 0x13, 0x26, - 0x07, 0xcb, 0x33, 0x10, 0xda, 0x4a, 0x56, 0x75, 0x00, 0x22, 0xf6, 0x94, 0x55, 0x39, 0x1c, 0xf5, 0x94, 0xd7, 0x03, - 0x9a, 0x90, 0x78, 0x53, 0x42, 0x47, 0x7c, 0x4d, 0x29, 0xd2, 0x44, 0xb5, 0x1b, 0xc8, 0x07, 0x4b, 0x69, 0xd1, 0xb1, - 0xbe, 0xbd, 0xd3, 0xb6, 0xab, 0xe5, 0xdd, 0x33, 0xfa, 0x6e, 0xe1, 0xc2, 0xdc, 0x29, 0x03, 0xc7, 0xd7, 0xcb, 0xb6, - 0x50, 0x61, 0x2c, 0x2c, 0x29, 0xab, 0x72, 0x0b, 0xe3, 0xcb, 0x0b, 0x7c, 0x0d, 0xba, 0x46, 0x31, 0xad, 0x72, 0xad, - 0x4f, 0xef, 0xd7, 0x39, 0x20, 0x3a, 0xc6, 0xa5, 0x11, 0xc1, 0x32, 0x3a, 0x3b, 0x6d, 0xa1, 0x75, 0x92, 0x5c, 0x96, - 0x34, 0x8a, 0xf0, 0x66, 0xee, 0x3f, 0xfa, 0x87, 0xf2, 0x2f, 0x53, 0xb4, 0x0a, 0x2c, 0x67, 0x1a, 0x5d, 0x48, 0x1f, - 0xe7, 0x41, 0xbb, 0x3d, 0xbb, 0x70, 0x17, 0xd5, 0x0c, 0xde, 0x75, 0x93, 0x51, 0x80, 0xcd, 0x1c, 0x90, 0x0e, 0x5d, - 0x75, 0x2c, 0x0f, 0xcc, 0xfa, 0x36, 0x86, 0x7e, 0xca, 0xf2, 0xcb, 0x05, 0x85, 0x93, 0xe2, 0xdf, 0xf1, 0x70, 0x54, - 0x46, 0xde, 0xa0, 0xc4, 0xc0, 0x62, 0x61, 0xf4, 0xea, 0x8a, 0x5e, 0x93, 0xce, 0x72, 0x6e, 0x8a, 0x79, 0xb8, 0x6b, - 0x1e, 0xcb, 0xde, 0xc7, 0x83, 0xd6, 0x49, 0xc7, 0x9b, 0x74, 0x17, 0x7a, 0x78, 0xce, 0xb3, 0xa9, 0x79, 0x9a, 0xcb, - 0x22, 0x36, 0x64, 0x63, 0x15, 0xb1, 0x94, 0xf5, 0xe2, 0xa4, 0xb6, 0xfc, 0x02, 0xb7, 0x1b, 0xd0, 0x36, 0x8b, 0x78, - 0x40, 0x4c, 0xdb, 0x33, 0xcf, 0x7b, 0x23, 0x3c, 0x49, 0xcf, 0x16, 0xc6, 0x5c, 0x3d, 0xd1, 0x14, 0xe3, 0x82, 0xf5, - 0xbc, 0x9f, 0xd2, 0xa7, 0xee, 0xe6, 0x50, 0x22, 0xac, 0xf0, 0x42, 0x1e, 0xa3, 0xbe, 0xab, 0xf9, 0xe3, 0x52, 0x14, - 0x83, 0x0b, 0xbc, 0xb2, 0x5e, 0xa8, 0x45, 0x51, 0xfb, 0x02, 0xac, 0x1d, 0x02, 0xd3, 0x6e, 0xb6, 0xa2, 0x42, 0x6c, - 0xf5, 0x2e, 0x7c, 0xa1, 0x6d, 0xef, 0x68, 0x36, 0xa3, 0x86, 0x2e, 0x70, 0x23, 0xd9, 0xd0, 0x28, 0x29, 0x28, 0x45, - 0x40, 0x9c, 0xc8, 0xcb, 0x36, 0x92, 0x6d, 0xc5, 0x93, 0x3c, 0xab, 0xa7, 0xdf, 0xb9, 0xfd, 0xff, 0x00, 0x52, 0x99, - 0xa6, 0xd8, 0x89, 0x7b, 0x00, 0x00}; + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xbd, 0x7d, 0xd9, 0x76, 0xdb, 0xc8, 0x92, 0xe0, 0xf3, + 0x9c, 0x33, 0x7f, 0x30, 0x2f, 0x30, 0x4a, 0x6d, 0x03, 0x25, 0x10, 0x22, 0x29, 0xcb, 0x76, 0x81, 0x02, 0x79, 0xe5, + 0xa5, 0xae, 0x5d, 0xe5, 0xad, 0x2c, 0xd9, 0x75, 0xab, 0x54, 0x2c, 0x0b, 0x22, 0x93, 0x22, 0xca, 0x20, 0xc0, 0x02, + 0x92, 0x5a, 0x8a, 0x42, 0x9f, 0x7e, 0xea, 0xa7, 0x39, 0x67, 0xd6, 0x87, 0x7e, 0x99, 0xd3, 0xfd, 0x30, 0x1f, 0x31, + 0xcf, 0xfd, 0x29, 0xf7, 0x07, 0xa6, 0x3f, 0x61, 0x22, 0x22, 0x17, 0x24, 0x40, 0x6a, 0x71, 0x75, 0xcd, 0x3d, 0x5e, + 0x04, 0xe4, 0x1a, 0x11, 0x19, 0x19, 0x5b, 0x46, 0x42, 0xbb, 0x77, 0xc6, 0xd9, 0x88, 0x5f, 0xcc, 0x99, 0x35, 0xe5, + 0xb3, 0xa4, 0xbf, 0x2b, 0xff, 0x67, 0xd1, 0xb8, 0xbf, 0x9b, 0xc4, 0xe9, 0x27, 0x2b, 0x67, 0x49, 0x18, 0x8f, 0xb2, + 0xd4, 0x9a, 0xe6, 0x6c, 0x12, 0x8e, 0x23, 0x1e, 0x05, 0xf1, 0x2c, 0x3a, 0x61, 0xd6, 0x56, 0x7f, 0x77, 0xc6, 0x78, + 0x64, 0x8d, 0xa6, 0x51, 0x5e, 0x30, 0x1e, 0xbe, 0x3f, 0xf8, 0xba, 0xf5, 0xa8, 0xbf, 0x5b, 0x8c, 0xf2, 0x78, 0xce, + 0x2d, 0x1c, 0x32, 0x9c, 0x65, 0xe3, 0x45, 0xc2, 0xfa, 0xa7, 0x51, 0x6e, 0x9d, 0xb3, 0xf0, 0xcd, 0xf1, 0x2f, 0x6c, + 0xc4, 0xfd, 0x31, 0x9b, 0xc4, 0x29, 0x7b, 0x9b, 0x67, 0x73, 0x96, 0xf3, 0x0b, 0xef, 0xd9, 0xfa, 0x8a, 0x98, 0x15, + 0xde, 0xbe, 0xae, 0x3a, 0x61, 0xfc, 0xcd, 0x59, 0xaa, 0xfa, 0x3c, 0x65, 0x62, 0x92, 0x2c, 0x2f, 0x3c, 0x7e, 0x45, + 0x9b, 0xfd, 0x8b, 0xd9, 0x71, 0x96, 0x14, 0xde, 0x27, 0x5d, 0x3f, 0xcf, 0x33, 0x9e, 0x21, 0x58, 0xfe, 0x34, 0x2a, + 0x8c, 0x96, 0xde, 0x93, 0x35, 0x4d, 0xe6, 0xb2, 0xf2, 0x45, 0xf1, 0x2c, 0x5d, 0xcc, 0x58, 0x1e, 0x1d, 0x27, 0xcc, + 0x2b, 0x58, 0xe8, 0x30, 0x8f, 0x7b, 0xb1, 0x1b, 0xf6, 0xb9, 0x15, 0xa7, 0x16, 0x1b, 0x9c, 0x33, 0x2a, 0x59, 0x32, + 0xdd, 0x2a, 0xb8, 0xd3, 0xf6, 0x80, 0x5c, 0x93, 0xf8, 0x64, 0xa1, 0xdf, 0xcf, 0xf2, 0x98, 0xab, 0xe7, 0xd3, 0x28, + 0x59, 0xb0, 0x20, 0x2e, 0xdd, 0x80, 0x1d, 0xf2, 0x61, 0x18, 0x7b, 0x4f, 0x68, 0x50, 0x18, 0x72, 0x39, 0xc9, 0x72, + 0x07, 0x69, 0x15, 0xe3, 0xd8, 0xfc, 0xf2, 0xd2, 0xe1, 0xe1, 0xb2, 0x74, 0xdd, 0x4f, 0xcc, 0x1f, 0x45, 0x49, 0xe2, + 0xe0, 0xc4, 0x77, 0xef, 0x16, 0x38, 0x63, 0xec, 0xf1, 0xc3, 0x78, 0xe8, 0xf6, 0xe2, 0x89, 0xc3, 0x99, 0x5b, 0xf5, + 0xcb, 0x26, 0x16, 0x67, 0x0e, 0x77, 0xdd, 0x27, 0x57, 0xf7, 0xc9, 0x19, 0x5f, 0xe4, 0x00, 0x7b, 0xe9, 0xbd, 0x51, + 0x33, 0x3f, 0xc3, 0xfa, 0x7d, 0xea, 0xd8, 0x03, 0xd8, 0x0b, 0x6e, 0x7d, 0x08, 0xcf, 0xe2, 0x74, 0x9c, 0x9d, 0xf9, + 0xfb, 0xd3, 0x08, 0x7e, 0xbc, 0xcb, 0x32, 0x7e, 0xf7, 0xae, 0x73, 0x9a, 0xc5, 0x63, 0xab, 0x1d, 0x86, 0x66, 0xe5, + 0xc5, 0x93, 0xfd, 0xfd, 0xcb, 0xcb, 0x46, 0x81, 0x9f, 0x46, 0x3c, 0x3e, 0x65, 0xa2, 0x33, 0x00, 0x60, 0xc3, 0xcf, + 0x39, 0x67, 0xe3, 0x7d, 0x7e, 0x91, 0x40, 0x29, 0x63, 0xbc, 0xb0, 0x01, 0xc7, 0xa7, 0xd9, 0x08, 0xc8, 0x96, 0x1a, + 0x84, 0x87, 0xa6, 0x39, 0x9b, 0x27, 0xd1, 0x88, 0x61, 0x3d, 0x8c, 0x54, 0xf5, 0xa8, 0x1a, 0x79, 0x5f, 0x87, 0x62, + 0x79, 0x1d, 0xd7, 0x8b, 0x59, 0x98, 0xb2, 0x33, 0xeb, 0x55, 0x34, 0xef, 0x8d, 0x92, 0xa8, 0x28, 0x80, 0x5f, 0x97, + 0x84, 0x42, 0xbe, 0x18, 0x01, 0x83, 0x10, 0x82, 0x4b, 0x24, 0xd3, 0x34, 0x2e, 0xfc, 0x8f, 0x1b, 0xa3, 0xa2, 0x78, + 0xc7, 0x8a, 0x45, 0xc2, 0x37, 0x42, 0x58, 0x0b, 0x7e, 0x27, 0x0c, 0xbf, 0x76, 0xf9, 0x34, 0xcf, 0xce, 0xac, 0x67, + 0x79, 0x0e, 0xcd, 0x6d, 0x98, 0x52, 0x34, 0xb0, 0xe2, 0xc2, 0x4a, 0x33, 0x6e, 0xe9, 0xc1, 0x70, 0x01, 0x7d, 0xeb, + 0x7d, 0xc1, 0xac, 0xa3, 0x45, 0x5a, 0x44, 0x13, 0x06, 0x4d, 0x8f, 0xac, 0x2c, 0xb7, 0x8e, 0x60, 0xd0, 0x23, 0x58, + 0xb2, 0x82, 0xc3, 0xae, 0xf1, 0x6d, 0xb7, 0x47, 0x73, 0x41, 0xe1, 0x01, 0x3b, 0xe7, 0x21, 0x2b, 0x81, 0x31, 0xad, + 0x42, 0xa3, 0xe1, 0xb8, 0xcb, 0x04, 0x0a, 0x58, 0x18, 0x33, 0x64, 0x59, 0xc7, 0x6c, 0xac, 0x17, 0xe7, 0xc3, 0xdd, + 0xbb, 0x9a, 0xd6, 0x40, 0x13, 0x07, 0xda, 0x16, 0x8d, 0xb6, 0x9e, 0x40, 0xbc, 0x46, 0x22, 0xd7, 0x63, 0xbe, 0x24, + 0xdf, 0xfe, 0x45, 0x3a, 0xaa, 0x8f, 0x0d, 0x95, 0x25, 0xcf, 0xf6, 0x79, 0x1e, 0xa7, 0x27, 0x00, 0x84, 0x9c, 0xc9, + 0x6c, 0x52, 0x96, 0x62, 0xf1, 0xdf, 0xb0, 0x90, 0x85, 0x7d, 0x1c, 0x3d, 0x67, 0x8e, 0x5d, 0x50, 0x0f, 0x3b, 0x0c, + 0x91, 0xf4, 0xc0, 0x60, 0x6c, 0xc0, 0x02, 0xb6, 0x69, 0xdb, 0xde, 0xd7, 0xae, 0x77, 0x81, 0x1c, 0xe4, 0xfb, 0x3e, + 0xb1, 0xaf, 0xe8, 0x1c, 0x87, 0x1d, 0x04, 0xda, 0x4f, 0x58, 0x7a, 0xc2, 0xa7, 0x03, 0x76, 0xd8, 0x1e, 0x06, 0x1c, + 0xa0, 0x1a, 0x2f, 0x46, 0xcc, 0x41, 0x7e, 0xf4, 0x72, 0xdc, 0x3e, 0x9b, 0x0e, 0x4c, 0x81, 0x0b, 0x73, 0x87, 0x70, + 0xac, 0x2d, 0x8d, 0xab, 0x58, 0x54, 0x01, 0x86, 0x7c, 0x6e, 0xc3, 0x0e, 0x3b, 0x66, 0xb9, 0x01, 0x87, 0x6e, 0xd6, + 0xab, 0xad, 0xe0, 0x02, 0x56, 0x08, 0xfa, 0x59, 0x93, 0x45, 0x3a, 0xe2, 0x31, 0x08, 0x2e, 0x7b, 0x13, 0xc0, 0x15, + 0x2b, 0xa7, 0x17, 0xce, 0x76, 0x4b, 0xd7, 0x89, 0xdd, 0x4d, 0x76, 0x98, 0x6f, 0x76, 0x86, 0x1e, 0x42, 0xa9, 0x89, + 0x2f, 0x11, 0x8f, 0x01, 0xc1, 0xd2, 0x7b, 0xcb, 0xf4, 0xf6, 0xfc, 0x30, 0x60, 0xfe, 0x2a, 0x1f, 0x87, 0xdc, 0x9f, + 0x45, 0x73, 0xc4, 0x86, 0x11, 0x0f, 0x44, 0xe9, 0x08, 0xa1, 0xab, 0xad, 0x0b, 0x52, 0xcc, 0xaf, 0x58, 0xc0, 0x05, + 0x82, 0xc0, 0x9e, 0x7d, 0x16, 0x8d, 0xa6, 0xb0, 0xc5, 0x2b, 0xc2, 0x8d, 0xd5, 0x76, 0x18, 0xe5, 0x2c, 0xe2, 0xec, + 0x59, 0xc2, 0xf0, 0x0d, 0x57, 0x00, 0x7a, 0xda, 0xae, 0x97, 0xab, 0x7d, 0x97, 0xc4, 0xfc, 0x75, 0x06, 0xf3, 0xf4, + 0x04, 0x93, 0x00, 0x17, 0xe7, 0x77, 0xef, 0xc6, 0xc8, 0x22, 0x7b, 0x1c, 0x56, 0xeb, 0x78, 0x01, 0x42, 0xc0, 0x4e, + 0xb1, 0x85, 0x0d, 0xd4, 0xf6, 0x62, 0x9f, 0x03, 0x11, 0x9f, 0x64, 0x29, 0x87, 0xe1, 0x00, 0x5e, 0xcd, 0x41, 0x7e, + 0x34, 0x9f, 0xb3, 0x74, 0xfc, 0x64, 0x1a, 0x27, 0x63, 0xa0, 0x46, 0x09, 0xf8, 0x66, 0x2c, 0x04, 0x3c, 0x01, 0x99, + 0xe0, 0x7a, 0x8c, 0x68, 0xf9, 0x90, 0x91, 0x79, 0x68, 0xdb, 0x3d, 0x94, 0x40, 0x12, 0x0b, 0x94, 0x41, 0xb4, 0x70, + 0xef, 0x40, 0xf4, 0x17, 0x2e, 0xdf, 0x0c, 0x63, 0xbd, 0x8c, 0x92, 0xc0, 0x6f, 0x50, 0xd2, 0x00, 0xfd, 0x19, 0xc8, + 0xc0, 0x1e, 0x0a, 0xae, 0xef, 0xa5, 0xd4, 0x49, 0x99, 0xc2, 0x10, 0x08, 0x30, 0x42, 0x09, 0x22, 0x69, 0xf0, 0x36, + 0x4b, 0x2e, 0x26, 0x71, 0x92, 0xec, 0x2f, 0xe6, 0xf3, 0x2c, 0xe7, 0xde, 0x37, 0xe1, 0x92, 0x67, 0x15, 0xae, 0xb4, + 0xc9, 0x8b, 0xb3, 0x98, 0x23, 0x41, 0xdd, 0xe5, 0x28, 0x82, 0xa5, 0x7e, 0x9c, 0x65, 0x09, 0x8b, 0x52, 0x40, 0x83, + 0x0d, 0x6c, 0x3b, 0x48, 0x17, 0x49, 0xd2, 0x3b, 0x86, 0x61, 0x3f, 0xf5, 0xa8, 0x5a, 0x48, 0xfc, 0x80, 0x9e, 0xf7, + 0xf2, 0x3c, 0xba, 0x80, 0x86, 0xd8, 0x06, 0x78, 0x11, 0x56, 0xeb, 0x9b, 0xfd, 0x37, 0xaf, 0x7d, 0xc1, 0xf8, 0xf1, + 0xe4, 0x02, 0x00, 0x2d, 0x2b, 0xa9, 0x39, 0xc9, 0xb3, 0x59, 0x63, 0x6a, 0xa4, 0x43, 0x1c, 0xb2, 0xde, 0x15, 0x20, + 0xc4, 0x34, 0x32, 0xac, 0x12, 0x33, 0x21, 0x78, 0x4d, 0xfc, 0x2c, 0x2b, 0x71, 0x0f, 0x0c, 0xf0, 0x21, 0x10, 0xc5, + 0x30, 0xe5, 0xf5, 0xd0, 0xf2, 0xfc, 0x62, 0x19, 0x87, 0x04, 0xe7, 0x1c, 0xf5, 0x2f, 0xc2, 0x38, 0x8a, 0x60, 0xf6, + 0xa5, 0x18, 0xb0, 0x54, 0x10, 0xc7, 0x65, 0xe9, 0x25, 0x9a, 0x89, 0x51, 0xe2, 0xa1, 0x40, 0xe1, 0xb0, 0x8d, 0x2e, + 0x2f, 0x19, 0xbc, 0xb8, 0xde, 0xb7, 0xe1, 0x32, 0x52, 0xf8, 0xa0, 0x86, 0xc2, 0xfd, 0x15, 0x08, 0x39, 0x81, 0x9a, + 0xec, 0x14, 0xf4, 0x20, 0xc0, 0xf9, 0x8d, 0x07, 0xfa, 0x3f, 0x41, 0x28, 0xee, 0x74, 0x3c, 0xd0, 0xa0, 0x4f, 0xa6, + 0x51, 0x7a, 0xc2, 0xc6, 0x41, 0xc2, 0x4a, 0x29, 0x79, 0xf7, 0x2c, 0x58, 0x63, 0x60, 0xa7, 0xc2, 0x7a, 0x7e, 0xf0, + 0xea, 0xa5, 0x5c, 0xb9, 0x9a, 0x30, 0x86, 0x45, 0x5a, 0x80, 0x5a, 0x05, 0xb1, 0x2d, 0xc5, 0xf1, 0x33, 0xae, 0xa4, + 0xb7, 0x28, 0x89, 0x8b, 0xf7, 0x73, 0x30, 0x31, 0xd8, 0x5b, 0x18, 0x06, 0xa6, 0x0f, 0x61, 0x2a, 0x2a, 0x87, 0xf9, + 0x44, 0xc5, 0x58, 0x17, 0x41, 0x67, 0x81, 0xa9, 0x78, 0xcd, 0x1c, 0xb7, 0x04, 0x56, 0xe5, 0xf1, 0xc8, 0x8a, 0xc6, + 0xe3, 0x17, 0x69, 0xcc, 0xe3, 0x28, 0x89, 0x7f, 0x23, 0x4a, 0x2e, 0x91, 0xc7, 0x78, 0x4f, 0x2e, 0x02, 0xe0, 0x4e, + 0x3d, 0x12, 0x57, 0x09, 0xd9, 0x3b, 0x44, 0x0c, 0x21, 0x2d, 0x93, 0xf0, 0x70, 0x28, 0xc1, 0x4b, 0xfc, 0xf9, 0xa2, + 0x98, 0x22, 0x61, 0xe5, 0xc0, 0x28, 0xc8, 0xb3, 0xe3, 0x82, 0xe5, 0xa7, 0x6c, 0xac, 0x39, 0xa0, 0x00, 0xac, 0xa8, + 0x39, 0x18, 0x2f, 0x34, 0xa3, 0xa3, 0x74, 0x28, 0x83, 0xa1, 0x7a, 0xa6, 0x98, 0x65, 0x92, 0x99, 0xb5, 0x85, 0xa3, + 0xa5, 0x80, 0x23, 0x8c, 0x0a, 0x29, 0x09, 0xf2, 0x50, 0x61, 0x38, 0x05, 0x29, 0x04, 0x5a, 0xc1, 0xdc, 0xe6, 0x4a, + 0x93, 0x3d, 0x5b, 0x90, 0x4a, 0xc8, 0xa1, 0x23, 0x6c, 0x64, 0x82, 0x34, 0x77, 0x61, 0x57, 0x81, 0x94, 0x97, 0xe0, + 0x0a, 0x29, 0xa2, 0xcc, 0x1c, 0x64, 0x80, 0xf0, 0x5b, 0xa1, 0x0b, 0x7d, 0x6c, 0x41, 0x6c, 0xe0, 0xeb, 0x95, 0x07, + 0xc2, 0x4a, 0xbc, 0x2b, 0x44, 0xbc, 0x2b, 0xc0, 0xc6, 0x89, 0x91, 0x9f, 0xbc, 0x3b, 0xdc, 0x4f, 0xb3, 0xbd, 0xd1, + 0x88, 0x15, 0x45, 0x06, 0xb0, 0xdd, 0xa1, 0xf6, 0x57, 0x19, 0x5a, 0x40, 0x49, 0x57, 0xcb, 0x3a, 0xbb, 0x20, 0x0d, + 0x6e, 0xaa, 0x15, 0xa5, 0xd3, 0x03, 0xfb, 0xe3, 0x47, 0x90, 0xd9, 0x9e, 0x24, 0x03, 0x50, 0x7d, 0xd5, 0xf0, 0x13, + 0xf6, 0x4c, 0x9d, 0x32, 0x6b, 0xed, 0x4b, 0xa7, 0x0e, 0x92, 0x07, 0xc3, 0xba, 0xa5, 0xb1, 0xa0, 0x6b, 0x87, 0xc6, + 0xd5, 0x90, 0x0a, 0x72, 0x79, 0x42, 0x2a, 0xdb, 0x58, 0x46, 0xb0, 0xda, 0x4a, 0x8f, 0x48, 0xaf, 0xb0, 0x29, 0x08, + 0xd0, 0x43, 0x36, 0xec, 0xc9, 0xfa, 0x30, 0x17, 0x94, 0xcb, 0xd9, 0xaf, 0x0b, 0x56, 0x70, 0xc1, 0xba, 0x30, 0x6e, + 0x01, 0xe3, 0x96, 0x2b, 0xd6, 0x61, 0xcd, 0x76, 0x5c, 0x07, 0xdb, 0x9b, 0x39, 0xea, 0xb1, 0x02, 0x39, 0xf9, 0x7a, + 0x76, 0x42, 0x58, 0x99, 0x7b, 0x79, 0xf9, 0xad, 0x1a, 0xa4, 0x5a, 0x4a, 0x6d, 0x03, 0x35, 0xd6, 0xc4, 0x56, 0x4d, + 0xc6, 0xb6, 0x2b, 0x15, 0xea, 0x9d, 0x4e, 0xaf, 0xc6, 0x07, 0xb0, 0xe7, 0xda, 0x9a, 0xa5, 0x2b, 0x63, 0xfb, 0xad, + 0xa2, 0xe9, 0x1b, 0x31, 0x32, 0x59, 0xa3, 0xec, 0x66, 0xee, 0x51, 0x3b, 0x1e, 0xda, 0xae, 0xd4, 0x55, 0x82, 0x61, + 0x51, 0x17, 0x0c, 0x4d, 0xa8, 0xe7, 0xba, 0x8b, 0xad, 0x99, 0x8a, 0x85, 0x6a, 0xad, 0x95, 0x03, 0xc1, 0xc3, 0x43, + 0x30, 0x4e, 0xd6, 0xfa, 0x07, 0xaf, 0xa3, 0x19, 0x43, 0x8a, 0x7a, 0x57, 0x35, 0x90, 0x0e, 0x04, 0x34, 0x19, 0x36, + 0xd5, 0x1b, 0x77, 0x85, 0xd5, 0x54, 0xdf, 0x5f, 0x31, 0x58, 0x11, 0x60, 0x5f, 0x97, 0x6b, 0x96, 0x88, 0xf4, 0xa6, + 0xe0, 0x12, 0x4d, 0x1f, 0x51, 0x26, 0xd6, 0x84, 0x14, 0x3c, 0x20, 0x0f, 0xcb, 0xdf, 0x58, 0x38, 0xd9, 0x8a, 0x29, + 0x1c, 0x39, 0xca, 0x14, 0xa0, 0x33, 0x29, 0x01, 0x10, 0x97, 0xf4, 0xb3, 0xb6, 0xb1, 0x90, 0x6c, 0xfb, 0xc8, 0x07, + 0xfe, 0x24, 0x89, 0xb8, 0xd3, 0xd9, 0x6a, 0xbb, 0xc0, 0x87, 0x20, 0xc4, 0x41, 0x47, 0x80, 0x79, 0x5f, 0xa1, 0xc2, + 0x10, 0x95, 0xd8, 0xe5, 0x3e, 0x18, 0x45, 0xd3, 0x78, 0xc2, 0x9d, 0x0c, 0x95, 0x88, 0x5b, 0xb2, 0x04, 0x94, 0x8c, + 0xde, 0x57, 0x20, 0x25, 0xb8, 0x90, 0x2e, 0xa2, 0x5a, 0x0b, 0x34, 0x05, 0x29, 0x49, 0x29, 0xd2, 0x82, 0x0a, 0x02, + 0x43, 0xa8, 0xf4, 0x14, 0x47, 0x81, 0x7e, 0x8b, 0x07, 0x62, 0xd0, 0x60, 0xc5, 0xa2, 0x8c, 0x07, 0xf1, 0x6a, 0x21, + 0xa8, 0x61, 0x9f, 0x67, 0x2f, 0xb3, 0x33, 0x96, 0x3f, 0x89, 0x10, 0xf6, 0x40, 0x74, 0x2f, 0x41, 0xd2, 0x93, 0x40, + 0x67, 0x3d, 0xc5, 0x2b, 0xa7, 0x84, 0x34, 0x2c, 0xc4, 0x2c, 0x46, 0x45, 0x08, 0x5a, 0x8e, 0x68, 0x9f, 0xe2, 0x96, + 0xa2, 0xbd, 0x87, 0xaa, 0x84, 0x69, 0xde, 0xda, 0x7b, 0x59, 0xe7, 0x2d, 0x18, 0x61, 0xae, 0xb8, 0xb5, 0xbe, 0x63, + 0x5d, 0x4f, 0xea, 0x66, 0x47, 0xf2, 0x96, 0xa1, 0xcc, 0x40, 0x7f, 0x5c, 0x5e, 0x56, 0x46, 0x3a, 0x28, 0x53, 0x2d, + 0xcd, 0xd1, 0x72, 0x12, 0x5b, 0xc2, 0x2d, 0x41, 0x19, 0xa1, 0xe1, 0x95, 0x67, 0x49, 0x62, 0xe8, 0x22, 0x2f, 0xee, + 0x39, 0x0d, 0x75, 0x04, 0x50, 0xcc, 0x6a, 0x1a, 0x69, 0xc0, 0x03, 0x5d, 0x81, 0x4a, 0x49, 0x69, 0x23, 0xaf, 0x6a, + 0x22, 0x20, 0x4e, 0xc7, 0x2c, 0x17, 0x0e, 0x9a, 0xd4, 0xa1, 0x30, 0x61, 0x0a, 0x0c, 0xcd, 0xc6, 0x20, 0xe1, 0x15, + 0x02, 0x60, 0x9e, 0xf8, 0xd3, 0xac, 0xe0, 0xba, 0xce, 0x84, 0x3e, 0xbe, 0xbc, 0x8c, 0x85, 0xbf, 0x88, 0x0c, 0x90, + 0xb3, 0x59, 0x76, 0xca, 0xd6, 0x40, 0xdd, 0x53, 0x83, 0x99, 0x20, 0x1b, 0xc3, 0x80, 0x12, 0x05, 0xd5, 0x32, 0x4f, + 0xe2, 0x11, 0xd3, 0x5a, 0x6a, 0xe6, 0x83, 0x41, 0xc7, 0xce, 0x41, 0x46, 0x30, 0xb7, 0xdf, 0xef, 0xb7, 0xbd, 0x8e, + 0x5b, 0x0a, 0x82, 0x2f, 0x57, 0x28, 0x7a, 0x8d, 0x7e, 0x94, 0x26, 0xf8, 0x3a, 0x59, 0xc0, 0x5d, 0x43, 0x29, 0x72, + 0xe1, 0x27, 0x79, 0x52, 0x10, 0xbb, 0xde, 0x18, 0x06, 0xe5, 0x4c, 0x09, 0x6e, 0x34, 0x71, 0xc5, 0xb6, 0x7d, 0xa7, + 0xc9, 0xa6, 0xd9, 0x49, 0xed, 0x30, 0xb5, 0x30, 0x72, 0xcd, 0x0b, 0xed, 0x01, 0x9b, 0xcb, 0x83, 0x56, 0x22, 0x55, + 0x03, 0xaf, 0x03, 0x84, 0xc2, 0xd3, 0x75, 0x56, 0x50, 0xaa, 0x3a, 0x4b, 0x21, 0xae, 0x37, 0xd0, 0x5b, 0x26, 0xc1, + 0x5c, 0x47, 0x82, 0x7d, 0x29, 0x10, 0x38, 0x7a, 0x64, 0x62, 0xbd, 0x9e, 0xc0, 0xf2, 0x1c, 0x47, 0xa3, 0x4f, 0x1a, + 0xdc, 0x8a, 0xec, 0x4d, 0x36, 0x70, 0x1a, 0x25, 0xa1, 0x21, 0xae, 0x4c, 0xbc, 0x95, 0x84, 0xae, 0x6d, 0x14, 0x70, + 0xc8, 0x56, 0xd8, 0xbe, 0xb9, 0xd0, 0x4d, 0x6e, 0x97, 0xec, 0xa1, 0xfc, 0x27, 0xcd, 0x25, 0xd7, 0xb0, 0x1c, 0x57, + 0xd2, 0x80, 0x2b, 0xc6, 0x83, 0xa5, 0x69, 0x40, 0x02, 0x7c, 0x57, 0x8e, 0xe3, 0xe2, 0x6a, 0x12, 0xfc, 0xa1, 0x60, + 0x3e, 0x35, 0x66, 0xba, 0x11, 0x52, 0x2d, 0xe1, 0xa4, 0x19, 0xac, 0x41, 0x93, 0xc6, 0x83, 0x12, 0x35, 0xdf, 0xa2, + 0xa1, 0x42, 0x1c, 0x7f, 0x22, 0xaa, 0xd0, 0x04, 0x43, 0x30, 0x72, 0xaf, 0x90, 0x0c, 0x97, 0xad, 0x8a, 0x16, 0x29, + 0x53, 0x63, 0x52, 0xa9, 0x9a, 0xe5, 0x32, 0x30, 0xb0, 0x68, 0xb7, 0xfa, 0xd2, 0x12, 0x57, 0x22, 0x37, 0x0d, 0xb5, + 0x30, 0x29, 0x94, 0x37, 0xe1, 0xe4, 0xe8, 0x77, 0x29, 0xeb, 0xdd, 0xc4, 0x27, 0x57, 0xf8, 0xe4, 0xbe, 0xe1, 0x43, + 0x99, 0xbc, 0x5d, 0x0c, 0x8a, 0xe0, 0x9b, 0x5a, 0x25, 0xda, 0xa7, 0x3e, 0x0a, 0x66, 0x57, 0x0b, 0x5d, 0x10, 0x28, + 0x92, 0x4d, 0xd2, 0x81, 0xe4, 0x37, 0x14, 0x1b, 0x95, 0x67, 0x94, 0xb9, 0x62, 0x83, 0xd4, 0xbc, 0xd2, 0xcc, 0x4b, + 0xdd, 0x86, 0xfd, 0x5e, 0x96, 0x92, 0x4e, 0x5c, 0x50, 0x26, 0xf6, 0xae, 0xa3, 0x8d, 0x97, 0x86, 0x99, 0xb0, 0x7e, + 0x85, 0xb1, 0x53, 0xa3, 0x50, 0x2a, 0x45, 0x20, 0x8e, 0x8d, 0xaf, 0x95, 0x65, 0x90, 0xf9, 0x6b, 0xec, 0x29, 0x00, + 0x25, 0x81, 0xc5, 0xd7, 0x54, 0xf2, 0xa2, 0xb0, 0x4e, 0xc7, 0x3b, 0x44, 0xc7, 0x4a, 0x84, 0xd6, 0x44, 0xbe, 0xd6, + 0x67, 0xb1, 0x5f, 0x73, 0x09, 0x4d, 0x4a, 0xe6, 0x83, 0x3c, 0xb0, 0x55, 0x20, 0xa2, 0xd2, 0x6d, 0xc9, 0x20, 0x21, + 0x87, 0x74, 0x95, 0xe8, 0xb5, 0x91, 0x0c, 0x5a, 0xa7, 0x42, 0xa2, 0xa5, 0xc3, 0x30, 0x72, 0xd0, 0x71, 0xa7, 0xb5, + 0x58, 0x21, 0x64, 0xd3, 0xde, 0x24, 0x56, 0x44, 0xe7, 0x34, 0x47, 0x13, 0xce, 0xd4, 0xe9, 0x8e, 0x03, 0xe8, 0x80, + 0xd8, 0x5f, 0x61, 0xbd, 0xb5, 0x66, 0xa7, 0xeb, 0x57, 0x0e, 0xdf, 0xe5, 0x65, 0x82, 0xfc, 0x20, 0x0c, 0x5e, 0x58, + 0xb3, 0x81, 0x92, 0xbd, 0x7b, 0x2f, 0xb1, 0x15, 0xd9, 0x9f, 0x55, 0x49, 0xe5, 0x29, 0xd4, 0x38, 0xb7, 0xbe, 0x4e, + 0xcc, 0x0c, 0x2d, 0xaa, 0x8a, 0x7d, 0x43, 0xaa, 0xef, 0x2b, 0x85, 0x5d, 0xa1, 0xbc, 0x2f, 0x87, 0x8e, 0x5d, 0xd7, + 0x0d, 0x72, 0x72, 0x5e, 0xee, 0xac, 0x73, 0x21, 0xef, 0xde, 0x35, 0x7d, 0xa6, 0x53, 0x3d, 0xfc, 0x13, 0x07, 0x95, + 0x73, 0x71, 0x91, 0x92, 0x05, 0xf3, 0x44, 0xa9, 0xa3, 0x15, 0x07, 0xb4, 0xdd, 0x43, 0x4f, 0x3b, 0x3a, 0x8b, 0x62, + 0x6e, 0xe9, 0x51, 0x84, 0xa7, 0x8d, 0xf2, 0x49, 0x1a, 0x1d, 0x80, 0x17, 0x9a, 0x90, 0xe4, 0x84, 0x9b, 0xb6, 0x68, + 0x31, 0x9a, 0x32, 0x0c, 0x81, 0x2b, 0x7b, 0xc2, 0x94, 0x3d, 0x77, 0x10, 0x6f, 0x31, 0x30, 0x5b, 0x0f, 0x7b, 0xd9, + 0xec, 0x5e, 0x33, 0xff, 0x61, 0x8d, 0x40, 0xb6, 0xcd, 0x54, 0x5d, 0xd9, 0x78, 0x97, 0x22, 0x12, 0x23, 0x6c, 0xeb, + 0xc6, 0x96, 0xb6, 0x7e, 0xaf, 0xe1, 0x5e, 0x57, 0x8e, 0x79, 0x4d, 0xa9, 0x36, 0xf4, 0xb0, 0x72, 0x73, 0x98, 0xe9, + 0xc8, 0x8b, 0x15, 0x74, 0x7b, 0x22, 0x28, 0x04, 0x4e, 0x84, 0xb6, 0x07, 0x15, 0x37, 0x10, 0x29, 0xb9, 0xd2, 0xaa, + 0xd9, 0x22, 0x19, 0x4b, 0x60, 0xc1, 0x85, 0xe5, 0x92, 0x8f, 0xce, 0xe2, 0x24, 0xa9, 0x4a, 0xff, 0x50, 0x01, 0x2f, + 0x86, 0xbd, 0x49, 0xb4, 0x0b, 0x8c, 0x16, 0x0a, 0x04, 0x57, 0x1b, 0x61, 0xef, 0x1d, 0xb7, 0x5a, 0x77, 0x11, 0x71, + 0xe4, 0x66, 0x34, 0x02, 0xea, 0x31, 0xc2, 0xaa, 0x59, 0x7b, 0xef, 0x19, 0x86, 0xd4, 0x0c, 0x7c, 0x50, 0x9d, 0x51, + 0xf1, 0x67, 0xd9, 0x53, 0x9f, 0x89, 0xde, 0x8d, 0xaa, 0xab, 0x19, 0x50, 0x51, 0x81, 0x0f, 0x33, 0xc4, 0xd2, 0x56, + 0x81, 0x80, 0x5c, 0x0f, 0x8b, 0x52, 0xc0, 0x24, 0x0d, 0x16, 0x94, 0x02, 0x6b, 0xad, 0xec, 0x5e, 0xde, 0x14, 0xcc, + 0xa1, 0x50, 0xb8, 0xe8, 0xff, 0x24, 0x9b, 0xcd, 0xd1, 0x32, 0x6b, 0x30, 0x35, 0x34, 0x78, 0xdf, 0xa8, 0x2f, 0xd7, + 0x94, 0xd5, 0xfa, 0xd0, 0x8e, 0xac, 0xf1, 0x93, 0x76, 0x94, 0xc1, 0xa1, 0x5a, 0xe8, 0xa2, 0xba, 0xdd, 0xdc, 0x14, + 0x31, 0xeb, 0x78, 0xdc, 0x27, 0xbd, 0xad, 0xad, 0x49, 0x4f, 0xd3, 0x80, 0x64, 0x92, 0x64, 0x78, 0x93, 0x01, 0xca, + 0x8a, 0x38, 0xcb, 0xb2, 0x41, 0xbe, 0x65, 0x59, 0xe2, 0xfa, 0x7d, 0xd7, 0xdb, 0xab, 0x79, 0xd6, 0xde, 0xde, 0xd5, + 0x2e, 0x72, 0x55, 0x27, 0x3d, 0xc8, 0xc3, 0x21, 0x14, 0xad, 0xd8, 0x94, 0xe1, 0x72, 0x96, 0x8d, 0x59, 0x60, 0x43, + 0xf7, 0xd4, 0x2e, 0x95, 0x56, 0x86, 0xcd, 0x91, 0x32, 0x67, 0xf9, 0xae, 0x1e, 0x49, 0x0d, 0xf6, 0x80, 0x05, 0xb4, + 0xb9, 0xf0, 0x7d, 0x78, 0x92, 0x64, 0xc7, 0x51, 0x72, 0x20, 0x14, 0x78, 0xad, 0xe5, 0x07, 0x70, 0x19, 0xc9, 0x62, + 0x35, 0x94, 0xd4, 0xf7, 0x83, 0xef, 0x83, 0x9b, 0x7b, 0x54, 0xde, 0x8a, 0xdd, 0xf1, 0xdb, 0x7e, 0xc7, 0x56, 0x11, + 0xb1, 0x97, 0xe6, 0x74, 0xa0, 0x71, 0x0a, 0xa0, 0xcc, 0x01, 0x68, 0xb2, 0xc2, 0x8b, 0x58, 0xf8, 0x72, 0xf0, 0x52, + 0xb9, 0xd4, 0x19, 0xb8, 0x10, 0xe0, 0xe4, 0x27, 0x31, 0x6f, 0xe1, 0x79, 0xa4, 0xed, 0x2d, 0x45, 0x05, 0xc6, 0x15, + 0x29, 0x2e, 0x5d, 0x2a, 0x6f, 0xd0, 0xfb, 0x18, 0x1e, 0x41, 0xb3, 0x8d, 0x8d, 0xa5, 0xf3, 0x2a, 0xe2, 0x53, 0x3f, + 0x8f, 0xd2, 0x71, 0x36, 0x73, 0xdc, 0x4d, 0xdb, 0x76, 0xfd, 0x82, 0x3c, 0x91, 0xaf, 0xdc, 0x72, 0xe3, 0xc8, 0x9b, + 0xb2, 0xd0, 0x1e, 0xd8, 0x9b, 0x1f, 0xbd, 0xf7, 0x2c, 0x3c, 0xda, 0xdd, 0x58, 0x4e, 0x59, 0xd9, 0x3f, 0xf2, 0xce, + 0x75, 0xcc, 0xdd, 0x7b, 0x8b, 0x52, 0x06, 0x7a, 0x85, 0xfd, 0x73, 0x09, 0x06, 0xb0, 0x1b, 0xc5, 0xdf, 0x41, 0xca, + 0xbd, 0xa7, 0x03, 0x11, 0x19, 0xa7, 0xbd, 0xbc, 0xb4, 0x33, 0x8a, 0x18, 0xd8, 0x77, 0xb4, 0xb3, 0x7a, 0xf7, 0x6e, + 0xa5, 0xe6, 0xab, 0x52, 0x6f, 0xc4, 0xc2, 0x9a, 0xa7, 0xee, 0x1d, 0xd0, 0xd1, 0x4a, 0x7d, 0x23, 0x8f, 0x18, 0x29, + 0xcd, 0x55, 0x3b, 0xc1, 0x31, 0xb6, 0xf8, 0xfa, 0x6d, 0x7d, 0x28, 0xa2, 0x14, 0x7e, 0x0c, 0xd6, 0x4b, 0x04, 0xea, + 0x1b, 0x1c, 0x1c, 0xef, 0x20, 0xdc, 0xda, 0x75, 0x06, 0x81, 0x73, 0xa7, 0xd5, 0xba, 0xfc, 0x69, 0xeb, 0xf0, 0xe7, + 0xa8, 0xf5, 0xdb, 0x5e, 0xeb, 0xc7, 0xa1, 0x7b, 0xe9, 0xfc, 0xb4, 0x35, 0x38, 0x94, 0x6f, 0x87, 0x3f, 0xf7, 0x7f, + 0x2a, 0x86, 0x5f, 0x8a, 0xc2, 0x0d, 0xd7, 0xdd, 0x3a, 0x01, 0x4f, 0x29, 0xdc, 0x6a, 0xb5, 0xfa, 0xf0, 0xb4, 0x80, + 0x27, 0xfc, 0x79, 0x06, 0x3f, 0x2e, 0x0f, 0xad, 0xff, 0xf0, 0x53, 0xfa, 0x1f, 0x7f, 0xca, 0x87, 0x38, 0xe6, 0xe1, + 0xcf, 0x3f, 0x15, 0xf6, 0xbd, 0x7e, 0xb8, 0x35, 0xdc, 0x74, 0x1d, 0x5d, 0xf3, 0x65, 0x58, 0x3d, 0x42, 0xab, 0xc3, + 0x9f, 0xe5, 0x9b, 0x7d, 0xef, 0x68, 0xb7, 0x1f, 0x0e, 0x2f, 0x1d, 0xfb, 0xf2, 0x9e, 0x7b, 0xe9, 0xba, 0x97, 0x1b, + 0x38, 0xcf, 0x1c, 0x46, 0xbf, 0x07, 0x3f, 0x4f, 0xe0, 0xa7, 0x0d, 0x3f, 0x4f, 0xe1, 0xe7, 0xcf, 0xd0, 0x4d, 0xc4, + 0xdf, 0x2e, 0x29, 0x16, 0x72, 0x89, 0x07, 0x16, 0x11, 0xac, 0x82, 0xbb, 0xb1, 0x15, 0x7b, 0x13, 0x22, 0x1a, 0xec, + 0x43, 0xdf, 0xf7, 0x31, 0x4c, 0xea, 0x2c, 0x3f, 0x6e, 0xc0, 0xa2, 0x23, 0xe7, 0x6c, 0x04, 0xcc, 0x13, 0x91, 0x83, + 0x22, 0xe0, 0xe2, 0x6c, 0xb5, 0xc0, 0xc3, 0x55, 0x6f, 0x1c, 0x4e, 0x98, 0x03, 0x46, 0xc1, 0x73, 0x86, 0x0f, 0x5d, + 0xd7, 0x7b, 0x26, 0xcf, 0x0c, 0x71, 0x9f, 0x0b, 0xd6, 0x4a, 0x33, 0x61, 0xd2, 0xd8, 0xae, 0x37, 0x5f, 0x53, 0x09, + 0xdb, 0x3a, 0x3d, 0x81, 0xba, 0x0d, 0x71, 0xd0, 0xf6, 0x3d, 0x8b, 0x3e, 0xe1, 0x96, 0x7c, 0x6d, 0x1c, 0x02, 0xaf, + 0x58, 0xf2, 0x4d, 0xa3, 0xd1, 0xb0, 0x11, 0x85, 0x3b, 0xf6, 0x98, 0xc1, 0x0c, 0x2b, 0x26, 0x22, 0x27, 0xa5, 0x29, + 0x2c, 0x5b, 0x98, 0xfc, 0x6d, 0x94, 0xf3, 0x8d, 0xca, 0xb0, 0x0d, 0x6b, 0x96, 0x6c, 0xd3, 0xd2, 0xbf, 0xc5, 0x14, + 0x68, 0x5a, 0xd2, 0xf9, 0x87, 0x39, 0x7e, 0x98, 0x12, 0x5a, 0xaf, 0x1d, 0x0e, 0x1e, 0x7a, 0x01, 0x72, 0x47, 0xf4, + 0x73, 0xde, 0xa2, 0x1a, 0x83, 0xbf, 0x32, 0xcc, 0xe0, 0x89, 0xf9, 0x30, 0x44, 0xb3, 0x2c, 0x75, 0x70, 0x2b, 0x45, + 0x71, 0xff, 0x02, 0x77, 0x46, 0x5a, 0x7a, 0xfb, 0xa1, 0xda, 0x31, 0x07, 0x39, 0x63, 0xdf, 0x47, 0xc9, 0x27, 0x96, + 0x3b, 0xe7, 0x5e, 0xa7, 0xfb, 0x15, 0x75, 0xf6, 0xd0, 0x36, 0x7b, 0x55, 0x1d, 0xa3, 0x29, 0xb3, 0x40, 0x1d, 0x11, + 0xb6, 0x3a, 0x5e, 0x8e, 0x51, 0x2d, 0x24, 0x41, 0xe1, 0x65, 0x61, 0x97, 0x38, 0xdc, 0xde, 0x2d, 0x4e, 0x4f, 0xfa, + 0x76, 0x60, 0xdb, 0x60, 0xf1, 0x1f, 0x50, 0xd8, 0x4a, 0x18, 0x16, 0x60, 0x90, 0xed, 0xc6, 0x3d, 0xbe, 0xb9, 0x59, + 0x05, 0x9c, 0xf0, 0x20, 0x9d, 0xba, 0x27, 0x5e, 0xe4, 0x4d, 0x43, 0x18, 0x70, 0x04, 0xcd, 0xb0, 0x4b, 0x6f, 0xb4, + 0x1b, 0xcb, 0x69, 0x30, 0x16, 0xe2, 0x27, 0x51, 0xc1, 0x5f, 0x60, 0x3c, 0x22, 0x1c, 0xa1, 0xb1, 0xef, 0xb3, 0x73, + 0x36, 0x52, 0x76, 0x06, 0x10, 0x2a, 0x72, 0x7b, 0xee, 0x28, 0x34, 0x9a, 0xc1, 0xdc, 0x61, 0x78, 0x30, 0xb0, 0x61, + 0x2f, 0xc1, 0xae, 0x0c, 0xa3, 0xc3, 0xce, 0x70, 0x90, 0x86, 0x20, 0x6b, 0x35, 0x6d, 0x65, 0xd1, 0xa2, 0x56, 0xd4, + 0x1d, 0x0e, 0x9c, 0x53, 0x30, 0xd2, 0xc1, 0x16, 0x77, 0xf0, 0x0d, 0x23, 0x14, 0x45, 0xf8, 0x8e, 0x9d, 0x3c, 0x3b, + 0x9f, 0x3b, 0xf6, 0xee, 0x96, 0xbd, 0x89, 0xa5, 0x9e, 0x0d, 0xec, 0x05, 0x73, 0x87, 0x67, 0xae, 0xd9, 0x79, 0x7b, + 0x88, 0xa0, 0x62, 0x21, 0x4e, 0x7e, 0x36, 0xb0, 0xfb, 0x62, 0xea, 0x36, 0x0c, 0x9a, 0xca, 0xe5, 0xc7, 0x15, 0x3d, + 0x20, 0x54, 0x55, 0x57, 0x05, 0x1d, 0x94, 0x75, 0x03, 0x67, 0x6a, 0x22, 0xd1, 0xc2, 0xc9, 0x24, 0x15, 0xc0, 0xe1, + 0xc1, 0x66, 0x30, 0xa9, 0xd1, 0x6d, 0x7b, 0x38, 0x38, 0x0b, 0xee, 0xd9, 0xf7, 0xd4, 0xcb, 0x09, 0x0b, 0xc0, 0xbb, + 0xa0, 0xe9, 0x4f, 0x50, 0x8b, 0xc0, 0xcf, 0x39, 0x03, 0x24, 0xcf, 0xa8, 0x68, 0x2c, 0x8b, 0x16, 0x58, 0x74, 0x10, + 0x20, 0xa8, 0x5e, 0xa1, 0xad, 0x3f, 0xb1, 0x26, 0xe3, 0x90, 0x60, 0xbf, 0x7b, 0x17, 0x96, 0x66, 0xb3, 0x33, 0xc4, + 0xf3, 0x86, 0x9c, 0x17, 0xdf, 0xc7, 0x1c, 0x54, 0xc2, 0x56, 0xdf, 0x76, 0x07, 0xb6, 0x85, 0x4b, 0xdb, 0xcb, 0x36, + 0x43, 0x41, 0xe1, 0x78, 0xf3, 0x3d, 0x0b, 0xa6, 0xfd, 0xb0, 0x3d, 0x70, 0x72, 0xa1, 0x3a, 0x12, 0x3c, 0xb7, 0x14, + 0x12, 0xbc, 0xed, 0x4d, 0x41, 0xa0, 0x23, 0xe7, 0x6e, 0xd8, 0x9b, 0xaa, 0x10, 0x8a, 0x3e, 0x6e, 0x8e, 0xdd, 0x20, + 0x86, 0x1f, 0x4e, 0x0b, 0x99, 0x66, 0xaa, 0xfb, 0x6a, 0xcd, 0xec, 0x06, 0x63, 0x65, 0x91, 0x27, 0x61, 0xb6, 0xe9, + 0x60, 0x84, 0x16, 0x24, 0xed, 0xee, 0x00, 0x60, 0xd8, 0x74, 0x14, 0xa7, 0x6d, 0x29, 0x56, 0x53, 0xf6, 0xf9, 0x61, + 0xb5, 0x1c, 0x6c, 0x10, 0x31, 0xbf, 0xd2, 0x3e, 0x00, 0x56, 0x90, 0x78, 0xf9, 0x50, 0x9d, 0x79, 0x3d, 0xaf, 0x9d, + 0x6f, 0x2d, 0x95, 0x28, 0x62, 0x9e, 0x21, 0xa1, 0x78, 0xa9, 0xdd, 0x30, 0x61, 0x6e, 0xcf, 0x91, 0x18, 0x9a, 0xe5, + 0xc3, 0x36, 0x30, 0xbd, 0x0a, 0xb0, 0xa7, 0xe6, 0xb6, 0x48, 0xc2, 0xaa, 0xb9, 0x77, 0x08, 0xac, 0x3d, 0x0c, 0x5f, + 0x89, 0x13, 0xc7, 0x9e, 0x8a, 0xe6, 0xb3, 0x24, 0x7c, 0xde, 0x38, 0x2e, 0x8e, 0xf0, 0x44, 0x68, 0xdf, 0x1f, 0x2d, + 0x72, 0x90, 0x07, 0xfc, 0x35, 0x58, 0x06, 0xa1, 0x6c, 0x8a, 0x8e, 0x1e, 0x1e, 0x01, 0x7b, 0x84, 0x78, 0x23, 0x6c, + 0x6e, 0x54, 0xa3, 0x45, 0x49, 0xc6, 0x0b, 0x1d, 0x0c, 0xf7, 0xb8, 0x74, 0xed, 0x51, 0x30, 0xc8, 0x13, 0x63, 0x07, + 0xcf, 0xfc, 0xfd, 0x11, 0x56, 0xe3, 0x04, 0x85, 0x5b, 0xd2, 0x6e, 0xab, 0xc4, 0xdf, 0xbe, 0x9f, 0x82, 0x04, 0xc7, + 0x3a, 0xf0, 0xb3, 0xee, 0xde, 0x4d, 0x24, 0x52, 0xbb, 0x69, 0x8f, 0x4e, 0x22, 0x30, 0x1e, 0x9c, 0xfb, 0x29, 0x54, + 0x23, 0x89, 0xa8, 0x28, 0x47, 0x0b, 0xd4, 0x3c, 0x55, 0xab, 0xe0, 0x3b, 0x34, 0x23, 0xf0, 0x1c, 0xc3, 0xd6, 0xe4, + 0xa7, 0xea, 0xc6, 0x22, 0x96, 0xef, 0xba, 0x74, 0xb4, 0x85, 0x07, 0x90, 0x82, 0xd1, 0x04, 0xc3, 0xb8, 0x14, 0x94, + 0xac, 0xf8, 0xef, 0xa3, 0x11, 0x2b, 0x9f, 0x1e, 0x66, 0x9b, 0x9b, 0x43, 0x71, 0x6e, 0x41, 0x8c, 0xc3, 0x8d, 0xe8, + 0x6a, 0x5c, 0x01, 0x50, 0x9f, 0xce, 0x89, 0xeb, 0x81, 0x69, 0xc5, 0x9a, 0x2e, 0xc5, 0x3e, 0x39, 0xcc, 0x00, 0x14, + 0xdc, 0x72, 0x0e, 0xfd, 0xc1, 0x9f, 0x86, 0xe0, 0x1e, 0xfb, 0x5f, 0xba, 0x5b, 0x4a, 0xd0, 0xf4, 0xe4, 0x99, 0xe2, + 0x92, 0xce, 0x58, 0x3b, 0x1e, 0xc5, 0x46, 0x83, 0xc2, 0x4b, 0x01, 0x03, 0xd0, 0xe6, 0x20, 0x13, 0x2a, 0x0e, 0x42, + 0x8e, 0x0a, 0x6c, 0x1f, 0x37, 0x3f, 0xc7, 0x9d, 0xfd, 0x14, 0x2c, 0xbc, 0x81, 0x7e, 0x7b, 0x0c, 0x6f, 0x7f, 0xd2, + 0x6f, 0x2f, 0x59, 0xf0, 0x4b, 0x29, 0x43, 0xf7, 0xb5, 0x29, 0x1e, 0xa8, 0x29, 0x4a, 0xb1, 0x44, 0x06, 0x0d, 0x99, + 0x9b, 0xaf, 0xc4, 0x6c, 0xb8, 0x5b, 0xa2, 0xda, 0x91, 0xa2, 0x2b, 0xf7, 0x79, 0x74, 0x82, 0xc4, 0x75, 0x4d, 0x52, + 0x18, 0xb9, 0x04, 0x26, 0xc2, 0x15, 0xdf, 0x12, 0x73, 0xf6, 0xdb, 0x60, 0x83, 0xd7, 0xf2, 0x0e, 0xd0, 0xbe, 0x63, + 0xb3, 0x39, 0xbf, 0xd8, 0x27, 0x45, 0x1f, 0xc8, 0xb4, 0x01, 0x71, 0x76, 0xde, 0xee, 0xc5, 0xbb, 0xbc, 0x17, 0x83, + 0x54, 0xcf, 0x15, 0x8b, 0xe1, 0x5e, 0xf5, 0xde, 0x62, 0x94, 0xd2, 0x64, 0x26, 0xaf, 0x86, 0x5e, 0x57, 0xa2, 0xb7, + 0xb9, 0x09, 0x08, 0xf6, 0x8c, 0xae, 0x5c, 0x74, 0x2d, 0x4b, 0x41, 0x13, 0x80, 0xe8, 0x51, 0x9d, 0xe5, 0x88, 0xe3, + 0x30, 0x9b, 0x0d, 0x05, 0x07, 0x73, 0xd7, 0x8e, 0x8a, 0x63, 0x62, 0x77, 0x99, 0xb0, 0x03, 0x98, 0x11, 0x97, 0xb7, + 0x3a, 0x22, 0x3a, 0x2c, 0xfa, 0xeb, 0xf8, 0xf6, 0x47, 0x8f, 0x6d, 0x76, 0x5c, 0xd0, 0x20, 0xb5, 0xb1, 0x1e, 0x56, + 0x63, 0x41, 0x7d, 0xf8, 0x51, 0x53, 0xa9, 0x2c, 0x36, 0x37, 0xcb, 0xfa, 0x51, 0xad, 0xda, 0xc1, 0xb5, 0xd3, 0x94, + 0xf3, 0x66, 0x36, 0x08, 0x07, 0x22, 0x26, 0x50, 0xa0, 0xa5, 0x95, 0x15, 0x03, 0x0c, 0x29, 0xcb, 0x51, 0x3e, 0x85, + 0xcc, 0x8b, 0xcb, 0x52, 0xa7, 0xbe, 0xc8, 0x78, 0x64, 0x88, 0xa7, 0x9e, 0x64, 0xac, 0x80, 0x82, 0xf5, 0x52, 0x2f, + 0xa1, 0x25, 0x02, 0xcc, 0x9f, 0xa9, 0x1c, 0x1a, 0x61, 0x81, 0x44, 0xa1, 0x61, 0x96, 0x28, 0xe3, 0xb3, 0x08, 0x63, + 0xd0, 0xf6, 0x4f, 0x6a, 0xb1, 0xaf, 0x42, 0x19, 0x1d, 0xc5, 0x61, 0x3e, 0x0c, 0xa8, 0x7e, 0x21, 0x25, 0xd8, 0x34, + 0x7c, 0x0f, 0x6c, 0x54, 0x39, 0x9e, 0x24, 0x08, 0x9f, 0xc6, 0x39, 0x23, 0x4f, 0x61, 0x43, 0xc2, 0x2c, 0x4d, 0xdb, + 0x48, 0xb5, 0x8b, 0xcc, 0x20, 0x94, 0x0b, 0xf3, 0x4f, 0x8d, 0xb3, 0x8b, 0x2c, 0x5c, 0x69, 0x0d, 0xe6, 0xc7, 0x1b, + 0x13, 0xa0, 0xec, 0xf2, 0x32, 0x13, 0x3e, 0x6e, 0x44, 0xf6, 0x86, 0xae, 0x98, 0x0e, 0x14, 0x52, 0x81, 0x13, 0x91, + 0xc5, 0x43, 0x67, 0x28, 0x34, 0xc2, 0x01, 0x9d, 0x22, 0xe7, 0xae, 0xb1, 0xe9, 0xf3, 0x81, 0xf6, 0x8d, 0xd2, 0xd0, + 0x49, 0x40, 0x08, 0x08, 0xdc, 0x0d, 0x6b, 0x2a, 0x1d, 0xa4, 0x41, 0x42, 0xa5, 0xe8, 0xe7, 0x00, 0xfe, 0x61, 0x24, + 0x29, 0x00, 0xf6, 0x43, 0x35, 0x52, 0x44, 0x59, 0x16, 0xb8, 0x00, 0x34, 0xd7, 0x3e, 0xae, 0x84, 0x2f, 0x0c, 0x54, + 0x98, 0x9e, 0x66, 0xe5, 0xa5, 0x50, 0x22, 0xef, 0xd6, 0xa4, 0xac, 0x91, 0x4c, 0x3e, 0x45, 0x87, 0x4f, 0x79, 0xd7, + 0xaf, 0x25, 0x1e, 0xba, 0xe0, 0x29, 0x2c, 0xab, 0x7a, 0x7e, 0x15, 0x72, 0x72, 0xae, 0x41, 0x57, 0x48, 0xa1, 0xbf, + 0xe2, 0x24, 0xef, 0xbd, 0xf2, 0xab, 0x5a, 0x6a, 0x0c, 0x65, 0xef, 0xd7, 0x35, 0xc3, 0xf2, 0x72, 0x5e, 0x85, 0x29, + 0x08, 0xb8, 0x25, 0x4b, 0x82, 0xa5, 0xd4, 0x10, 0x60, 0x61, 0x7b, 0xa4, 0x95, 0x82, 0xbc, 0xd4, 0xe1, 0x9d, 0xa7, + 0x60, 0x05, 0x18, 0x87, 0x5a, 0x2a, 0x99, 0x46, 0x12, 0x5f, 0x2a, 0x51, 0x60, 0xca, 0xfd, 0x11, 0xf8, 0xa9, 0xcd, + 0x93, 0xae, 0x73, 0xd7, 0x8f, 0x67, 0x98, 0xda, 0x43, 0xa0, 0xc7, 0xde, 0x1d, 0x30, 0x25, 0xea, 0x3a, 0xac, 0x20, + 0x0e, 0xcd, 0x6a, 0x9a, 0x05, 0xcc, 0x98, 0x36, 0x68, 0xc9, 0x36, 0xd8, 0x72, 0x39, 0xd8, 0x47, 0x62, 0x7b, 0x56, + 0x2b, 0x20, 0x74, 0x0d, 0x1a, 0x18, 0x72, 0x97, 0x0a, 0x2d, 0xcc, 0x7b, 0x5d, 0x2a, 0xc2, 0xfd, 0x39, 0xe0, 0xd2, + 0x0a, 0xce, 0xbc, 0x8c, 0x06, 0xde, 0x8f, 0x8f, 0x13, 0x4c, 0x7c, 0x41, 0xac, 0xc0, 0x0e, 0x0e, 0x3a, 0xcd, 0xa6, + 0xc0, 0xa9, 0xb8, 0x48, 0x19, 0x2c, 0x2b, 0x4a, 0x6d, 0xf8, 0x21, 0x45, 0xb6, 0xee, 0xf2, 0x40, 0x77, 0x21, 0x16, + 0xc0, 0x4e, 0xbf, 0x60, 0xe4, 0x5b, 0xd6, 0xcb, 0x80, 0xc1, 0xa9, 0xd6, 0x38, 0x08, 0xfc, 0xe6, 0x66, 0x32, 0x2c, + 0x53, 0x62, 0xbb, 0x26, 0xab, 0x0b, 0xc8, 0x61, 0xa8, 0x26, 0xee, 0x20, 0x2c, 0x95, 0x3d, 0x5e, 0x94, 0x33, 0x5c, + 0x2e, 0x65, 0x21, 0x37, 0xcf, 0xab, 0x69, 0x3e, 0xb7, 0xd2, 0x6c, 0x3a, 0xde, 0x8a, 0x2f, 0x0a, 0xfe, 0x81, 0x13, + 0x4b, 0xab, 0x9e, 0x52, 0x2b, 0x3c, 0xca, 0xdc, 0x92, 0x75, 0x4a, 0x6a, 0x75, 0xdd, 0x40, 0x35, 0xc2, 0xd3, 0x34, + 0x6c, 0x04, 0x42, 0x4c, 0x70, 0xf1, 0xeb, 0x26, 0x13, 0xd3, 0xde, 0x12, 0x52, 0x47, 0xd8, 0x3d, 0x94, 0x13, 0xdc, + 0xd5, 0x3c, 0xfb, 0x3c, 0x9c, 0x5f, 0xcd, 0xdc, 0x7b, 0x06, 0x73, 0x3f, 0x0e, 0xb9, 0xc1, 0xe8, 0xb1, 0x4c, 0xf8, + 0x91, 0xb1, 0x8f, 0x5c, 0x55, 0x3d, 0x39, 0x09, 0x2b, 0x91, 0x25, 0x9e, 0x8c, 0xa3, 0x0e, 0xe3, 0x54, 0xb4, 0x26, + 0xc8, 0x2e, 0x2f, 0x0b, 0x73, 0x2f, 0x50, 0xd0, 0xd4, 0xe3, 0xf5, 0x38, 0x6d, 0xc5, 0xce, 0x46, 0x24, 0x72, 0xef, + 0x55, 0x2d, 0x12, 0x59, 0xf1, 0x39, 0x8e, 0x74, 0xc5, 0x41, 0xee, 0x93, 0x93, 0xd5, 0x4d, 0x2a, 0x74, 0x8b, 0x46, + 0xdb, 0xd8, 0xa3, 0xfa, 0x40, 0x52, 0xcf, 0xa8, 0xc0, 0xaa, 0xc6, 0xbe, 0x7b, 0xb7, 0x23, 0xd2, 0x2d, 0x95, 0x62, + 0x83, 0xa5, 0x85, 0xd1, 0x8c, 0x51, 0x30, 0x28, 0x29, 0x32, 0x50, 0xa3, 0xfc, 0x0a, 0xc1, 0xb0, 0x47, 0x0d, 0x40, + 0x71, 0xae, 0xaf, 0x7e, 0x5c, 0x4a, 0xb6, 0x10, 0x90, 0xb8, 0x4b, 0x06, 0x62, 0x4d, 0x30, 0x33, 0xf2, 0xc9, 0x7b, + 0xe0, 0xbc, 0x01, 0x43, 0x1f, 0x01, 0xfc, 0x02, 0xb1, 0xe9, 0xc1, 0xc4, 0xb6, 0x89, 0x28, 0xfa, 0x6c, 0xe0, 0x39, + 0x00, 0x3b, 0xaf, 0x42, 0xa3, 0xef, 0xaa, 0x14, 0x30, 0x64, 0x03, 0x37, 0x60, 0x55, 0x58, 0x6e, 0xef, 0x39, 0xb8, + 0x0d, 0xf0, 0xfa, 0x4c, 0x36, 0xdf, 0xc0, 0x3c, 0xc1, 0xea, 0xec, 0xc2, 0xaf, 0x2c, 0x6b, 0x71, 0xee, 0x74, 0xd0, + 0xa8, 0x57, 0x94, 0x10, 0xb5, 0xfb, 0x58, 0x7b, 0x80, 0x11, 0x16, 0xf1, 0xfe, 0x0a, 0xdf, 0xf5, 0xb8, 0xe5, 0x9e, + 0x46, 0x8b, 0x30, 0x5d, 0x25, 0x8d, 0x41, 0xc9, 0xba, 0x9f, 0x8c, 0xb8, 0x97, 0xfb, 0x22, 0x16, 0x5c, 0xe1, 0xc8, + 0xaa, 0x90, 0x62, 0x03, 0x49, 0x7a, 0xda, 0xa3, 0x03, 0xf6, 0x8d, 0x66, 0x2f, 0xa0, 0xcc, 0xfb, 0x8a, 0x54, 0x12, + 0x52, 0x9a, 0xdd, 0x10, 0x49, 0xc2, 0x5a, 0x91, 0xa7, 0xce, 0xfb, 0x8e, 0xf6, 0xb9, 0x95, 0x44, 0x30, 0x82, 0x93, + 0x30, 0x1d, 0x2b, 0x0f, 0x9a, 0x02, 0x5c, 0x45, 0x47, 0x4c, 0xdf, 0x04, 0xe4, 0x37, 0x03, 0xb9, 0xbd, 0x92, 0x5c, + 0x9b, 0x6b, 0x18, 0x9e, 0x20, 0xc1, 0xaa, 0x48, 0x04, 0x1e, 0x51, 0x03, 0x8e, 0xf9, 0x3a, 0xcf, 0x03, 0x4c, 0xf8, + 0xda, 0xde, 0x04, 0x80, 0x72, 0x72, 0x55, 0x9c, 0x95, 0x40, 0x37, 0x60, 0xb9, 0x3e, 0x4e, 0x8d, 0x8a, 0xc4, 0xc5, + 0x8d, 0xe9, 0xea, 0x96, 0xfe, 0x0c, 0x2d, 0x67, 0x32, 0xc4, 0x74, 0x10, 0x04, 0x64, 0xea, 0x3b, 0xe6, 0x08, 0x99, + 0x2b, 0xac, 0xcf, 0xb9, 0x53, 0x9b, 0xba, 0xc7, 0xa8, 0x9b, 0x27, 0xa9, 0xc5, 0xeb, 0xb4, 0x29, 0x25, 0x62, 0x52, + 0x62, 0x6e, 0x88, 0x54, 0x6c, 0xa6, 0xc4, 0x9d, 0x5b, 0xdf, 0x68, 0x21, 0x6d, 0xb4, 0x0d, 0x91, 0x83, 0xcd, 0x2a, + 0x79, 0x4f, 0x60, 0x3c, 0x17, 0x84, 0x2f, 0x5f, 0x51, 0x92, 0x0e, 0x73, 0x4c, 0x04, 0xab, 0x17, 0x53, 0x91, 0xbf, + 0x73, 0x74, 0x9a, 0xbd, 0x41, 0x0f, 0x52, 0x6f, 0x20, 0x31, 0x6b, 0xe2, 0xbb, 0x90, 0x86, 0x3a, 0x42, 0xa0, 0x32, + 0xaa, 0x65, 0x3a, 0x4e, 0xac, 0xc2, 0x37, 0x82, 0xaf, 0xde, 0xea, 0xe3, 0x7c, 0xe3, 0xb9, 0xb1, 0x1a, 0x41, 0x0c, + 0xde, 0x42, 0x3e, 0xf4, 0xa4, 0x08, 0x07, 0xc2, 0xe5, 0x9b, 0x9b, 0xbd, 0x7c, 0x97, 0x57, 0x21, 0x92, 0x0a, 0xc6, + 0x18, 0x33, 0x8a, 0x71, 0x4f, 0xd4, 0xd4, 0x62, 0x0e, 0x03, 0xcb, 0xd6, 0x61, 0x8e, 0x07, 0x00, 0xd0, 0xd2, 0x94, + 0x5e, 0x35, 0x15, 0x2a, 0xcf, 0x73, 0x09, 0x9f, 0xea, 0x10, 0x55, 0x35, 0x7e, 0xbb, 0x3e, 0x03, 0x85, 0xe0, 0xbe, + 0xd3, 0xf1, 0xf0, 0x10, 0x02, 0x56, 0x51, 0xc8, 0x02, 0xbd, 0x41, 0x7b, 0x55, 0x22, 0x14, 0x33, 0x27, 0xeb, 0x31, + 0xc3, 0x49, 0x05, 0x5b, 0xa8, 0x84, 0xa5, 0xd2, 0x02, 0xbf, 0xda, 0x08, 0xcd, 0x53, 0xc6, 0xbd, 0x57, 0x15, 0xce, + 0xa0, 0x3f, 0x98, 0xb7, 0xca, 0xa8, 0x6f, 0x57, 0x4e, 0x64, 0x2a, 0x30, 0x71, 0x33, 0x4b, 0xed, 0xf7, 0xcb, 0x3a, + 0xed, 0xe7, 0x15, 0x72, 0x9f, 0x93, 0xe6, 0xeb, 0xdc, 0x42, 0xf3, 0xc9, 0x70, 0xbf, 0x52, 0x7e, 0x68, 0x61, 0xd4, + 0x94, 0x5f, 0x5e, 0x57, 0x7e, 0x85, 0xa7, 0xc2, 0x5b, 0xfd, 0x2e, 0x0a, 0x5d, 0xd4, 0xe7, 0x60, 0x08, 0xe9, 0x47, + 0x70, 0x0d, 0x0d, 0x1e, 0x14, 0xc9, 0x62, 0xb1, 0x76, 0x41, 0x5c, 0x1f, 0x73, 0xaa, 0x1d, 0xca, 0x18, 0x23, 0x9e, + 0x96, 0x1c, 0x24, 0x19, 0x1c, 0x8c, 0xdf, 0xc0, 0x80, 0x98, 0x94, 0x84, 0x74, 0x08, 0x9d, 0xb5, 0x99, 0x88, 0xca, + 0x5d, 0xbc, 0xd9, 0xb8, 0xac, 0x29, 0x14, 0x61, 0x27, 0x98, 0xa9, 0x94, 0x0a, 0x02, 0x69, 0xf2, 0xdd, 0xe9, 0xd4, + 0x82, 0xa1, 0x85, 0x6b, 0x2a, 0x20, 0xaf, 0xed, 0x7a, 0xd0, 0xe4, 0x3d, 0xc5, 0xd0, 0xd7, 0xa9, 0x11, 0x2f, 0x33, + 0xf8, 0x1a, 0x36, 0x7f, 0x4d, 0x94, 0xe4, 0x21, 0x13, 0xb1, 0x57, 0xf0, 0x89, 0x90, 0x4d, 0xc1, 0xce, 0x04, 0xfa, + 0xa1, 0x5d, 0xd9, 0x4b, 0x77, 0x8b, 0xca, 0xa5, 0x45, 0x63, 0x2b, 0x51, 0xb3, 0xe6, 0x87, 0xf1, 0x66, 0x0a, 0xfb, + 0xd9, 0xa3, 0x04, 0x02, 0xd2, 0x54, 0x4e, 0x52, 0xcd, 0x7b, 0x98, 0x0e, 0x01, 0x24, 0xd8, 0xfd, 0x04, 0x16, 0xfa, + 0x4d, 0x89, 0x09, 0x16, 0x55, 0x63, 0xb7, 0x39, 0x68, 0xcd, 0x39, 0x69, 0xbe, 0x39, 0x6a, 0xed, 0x4d, 0x65, 0x3d, + 0x63, 0x76, 0x80, 0x6d, 0xbb, 0x9b, 0xc5, 0x61, 0xba, 0xd9, 0x19, 0x1a, 0x82, 0x0b, 0x8f, 0xff, 0x93, 0x12, 0xd3, + 0x40, 0x72, 0xa9, 0x1b, 0x3f, 0xa1, 0x0e, 0xc3, 0xff, 0x96, 0xa4, 0x80, 0x07, 0xb5, 0xd5, 0x58, 0x71, 0xee, 0x15, + 0x47, 0xc9, 0x65, 0x55, 0xed, 0x6a, 0x09, 0x1a, 0xba, 0x91, 0x8c, 0x89, 0x62, 0x9e, 0x13, 0x00, 0xa3, 0xd8, 0xfc, + 0x29, 0xd3, 0x49, 0xde, 0xbf, 0xac, 0x4d, 0xed, 0xf6, 0x7d, 0x3f, 0xca, 0x4f, 0xe8, 0x48, 0x45, 0x65, 0x73, 0x12, + 0xf3, 0x6f, 0x0b, 0x30, 0xcd, 0x89, 0x0f, 0xf5, 0x5c, 0xc3, 0x50, 0x80, 0xaf, 0x6c, 0x28, 0x35, 0xdb, 0xe3, 0xdf, + 0x3b, 0xdb, 0x7d, 0x49, 0x14, 0xc1, 0x02, 0x0d, 0xba, 0x5c, 0x81, 0x2f, 0x60, 0x19, 0xdc, 0x92, 0x7e, 0x0a, 0xbe, + 0x97, 0x57, 0xc1, 0x67, 0xec, 0x7f, 0x01, 0x68, 0x55, 0x60, 0x40, 0xb9, 0xd3, 0x34, 0xac, 0x84, 0xb8, 0x44, 0x85, + 0x59, 0xc5, 0xf9, 0xe3, 0x3a, 0xaf, 0x9b, 0x96, 0x25, 0x06, 0xe5, 0xe7, 0xae, 0xe1, 0xc6, 0xf7, 0x1a, 0xf9, 0xe3, + 0x7b, 0xcf, 0x41, 0xb7, 0x13, 0x69, 0xef, 0xde, 0xcd, 0xef, 0x90, 0x85, 0x86, 0xf7, 0xc2, 0xe6, 0xd0, 0x16, 0xe9, + 0x92, 0xab, 0x67, 0x2c, 0xc6, 0xdb, 0x22, 0x54, 0x86, 0x0f, 0x58, 0x30, 0x07, 0x0c, 0xc1, 0x63, 0xa7, 0x32, 0xf9, + 0x0c, 0x1b, 0x4d, 0xb1, 0x6b, 0x2e, 0x0c, 0x3e, 0x50, 0x95, 0x85, 0xe4, 0xc5, 0x3a, 0xd9, 0x9e, 0x9d, 0xc2, 0xf3, + 0xcb, 0xb8, 0x00, 0xea, 0x00, 0xfa, 0x15, 0x95, 0xc5, 0x06, 0x72, 0x71, 0x53, 0xd6, 0x7a, 0x45, 0xe3, 0xf1, 0xb5, + 0x5d, 0x58, 0x5d, 0x81, 0x4f, 0xa3, 0x74, 0x9c, 0x88, 0x49, 0xcc, 0xa4, 0xca, 0x35, 0xb9, 0x36, 0xba, 0x97, 0xb6, + 0x68, 0x9e, 0x0b, 0x09, 0x5e, 0x11, 0xb8, 0x21, 0xf4, 0x95, 0xbe, 0x5c, 0x6f, 0xa0, 0xe0, 0x51, 0x7b, 0x73, 0x11, + 0x4c, 0x4c, 0x3c, 0x66, 0x48, 0x4d, 0xbf, 0x0e, 0xa7, 0x56, 0x16, 0x2b, 0x0e, 0xbf, 0xce, 0x19, 0x6b, 0x28, 0x00, + 0xe2, 0x93, 0x07, 0x57, 0xbb, 0x49, 0xaf, 0x94, 0x76, 0x50, 0x1a, 0x21, 0xbe, 0xad, 0xf0, 0x75, 0x97, 0x8a, 0xaf, + 0x5c, 0x75, 0xef, 0x6b, 0xc6, 0x8c, 0x0b, 0x46, 0xcf, 0xf9, 0x2c, 0x69, 0x5c, 0xbb, 0xa1, 0xbb, 0x3a, 0x3f, 0x7a, + 0x3f, 0xc8, 0xbc, 0x85, 0x19, 0xb0, 0x09, 0xa8, 0x82, 0xe7, 0xde, 0x6b, 0xe3, 0x44, 0xf9, 0x3b, 0xf3, 0x88, 0x57, + 0x0e, 0xb3, 0xee, 0x24, 0xf9, 0xbb, 0xc1, 0x77, 0xc1, 0xd5, 0x2d, 0x8d, 0x13, 0xe4, 0xae, 0x3a, 0x41, 0x26, 0xca, + 0xcd, 0xf4, 0x86, 0xdb, 0xbb, 0xad, 0x40, 0x10, 0xa7, 0x62, 0xfa, 0xa8, 0x1c, 0xd7, 0x8f, 0x16, 0xa8, 0x54, 0x44, + 0x7c, 0xaa, 0x72, 0x57, 0xae, 0x4c, 0x0d, 0xf5, 0xb8, 0x4e, 0x66, 0xa1, 0x69, 0xd6, 0xe4, 0x52, 0x36, 0x3d, 0x46, + 0xa6, 0xd9, 0xa9, 0x36, 0xbf, 0x7b, 0xe5, 0x21, 0x1d, 0x43, 0x73, 0xb1, 0x56, 0x0b, 0xee, 0x77, 0x15, 0x85, 0x77, + 0xbd, 0xd8, 0x48, 0x65, 0xa8, 0x59, 0x8f, 0xa2, 0x8f, 0xe3, 0x36, 0x73, 0x79, 0x94, 0xfd, 0x59, 0x03, 0xc0, 0x74, + 0x84, 0x45, 0x77, 0xd3, 0x33, 0xf6, 0x04, 0x7a, 0x7a, 0x22, 0x83, 0x44, 0xaf, 0x75, 0xbe, 0x6a, 0x95, 0x58, 0xba, + 0x86, 0xc0, 0xee, 0x35, 0x19, 0xab, 0x92, 0x76, 0xab, 0xf5, 0xab, 0x79, 0x3e, 0x4f, 0xf9, 0x4a, 0x9e, 0x4f, 0xcd, + 0xa2, 0xbb, 0xd3, 0x76, 0xaf, 0x4f, 0x0d, 0x15, 0x73, 0xad, 0x6f, 0xf2, 0x3b, 0xa6, 0xeb, 0x60, 0xa8, 0x45, 0x90, + 0x59, 0xed, 0xaa, 0x67, 0x65, 0x39, 0xab, 0x67, 0x72, 0xcc, 0x84, 0x6f, 0x2a, 0xdd, 0x21, 0xba, 0x61, 0xaa, 0x66, + 0xfa, 0xb1, 0xb1, 0x2d, 0x64, 0x9b, 0xe7, 0x17, 0xe3, 0x1c, 0x28, 0x2d, 0xf7, 0x97, 0x09, 0xc3, 0x8f, 0x97, 0x97, + 0x3f, 0x0a, 0x39, 0x55, 0x75, 0xf4, 0x96, 0x2f, 0x75, 0xcf, 0x60, 0x56, 0x2a, 0x27, 0xe2, 0x98, 0xad, 0x1f, 0xbc, + 0xb9, 0x7b, 0x05, 0x2c, 0xc7, 0x80, 0xdd, 0x31, 0x73, 0x1a, 0x43, 0x55, 0x1b, 0xf8, 0x87, 0xf5, 0x83, 0xad, 0xdb, + 0xc3, 0x3f, 0x0c, 0x7e, 0x08, 0xae, 0x6d, 0x6c, 0x6c, 0xe3, 0xed, 0x5a, 0x22, 0xc8, 0x2b, 0x3c, 0xd0, 0xc7, 0xab, + 0x8f, 0x82, 0x96, 0xeb, 0xc4, 0xf6, 0xc0, 0xa1, 0xb0, 0x35, 0xc8, 0x37, 0x29, 0x93, 0x46, 0x8b, 0x82, 0x67, 0x33, + 0x39, 0x43, 0x21, 0xaf, 0xf9, 0x38, 0x68, 0x3b, 0xc2, 0xdf, 0xc0, 0xa9, 0x1d, 0x2f, 0x2f, 0x3f, 0x41, 0x1f, 0xf0, + 0x74, 0xa5, 0x34, 0x15, 0x71, 0x4a, 0xb9, 0x45, 0x57, 0xeb, 0x3c, 0x18, 0x29, 0x2e, 0xa6, 0xa8, 0x74, 0xdc, 0xe5, + 0xb5, 0xb3, 0x91, 0xd3, 0x5f, 0xe2, 0xd5, 0x45, 0xba, 0x7c, 0x24, 0xb2, 0x55, 0x4b, 0xef, 0x37, 0x7d, 0xba, 0x6d, + 0xcf, 0x18, 0x9f, 0x66, 0x63, 0x3a, 0x98, 0xf1, 0x71, 0x22, 0xbc, 0x3e, 0x31, 0xd6, 0x77, 0x8b, 0xc0, 0x74, 0x73, + 0x6c, 0xf2, 0xc3, 0xf1, 0x7a, 0xb3, 0x59, 0xe3, 0x0e, 0xde, 0x38, 0x4f, 0x9c, 0x65, 0x89, 0x11, 0x95, 0xa5, 0x86, + 0x07, 0xb4, 0x42, 0xdc, 0xbc, 0x67, 0x02, 0xe3, 0xb2, 0x0b, 0x92, 0xda, 0x6e, 0x20, 0x70, 0xb1, 0x27, 0x31, 0x4b, + 0xc6, 0xb6, 0x07, 0xe5, 0x81, 0xbe, 0x18, 0x4d, 0xb7, 0x80, 0x69, 0x79, 0xed, 0xec, 0x2c, 0xb5, 0xbd, 0x6a, 0xaa, + 0x00, 0x66, 0xc9, 0xf2, 0xf8, 0x04, 0x59, 0xf7, 0x5b, 0xe8, 0x22, 0x06, 0x8c, 0x8d, 0x2b, 0x73, 0xee, 0x72, 0xdd, + 0x8a, 0xf8, 0x46, 0x13, 0x69, 0x52, 0x1f, 0x52, 0xdf, 0x61, 0x58, 0xab, 0xab, 0x1c, 0x24, 0x70, 0x8f, 0xbc, 0x5b, + 0xe2, 0xd2, 0xd3, 0x67, 0x16, 0x93, 0x2a, 0x7d, 0x4b, 0x5d, 0x8b, 0x6b, 0x86, 0xbd, 0xe2, 0x01, 0xd8, 0x1f, 0x18, + 0xb7, 0x88, 0x45, 0xbc, 0x9d, 0xd7, 0x52, 0x58, 0x1b, 0x73, 0xa0, 0xb9, 0xe1, 0x06, 0xbf, 0xb1, 0x6a, 0xcd, 0xc0, + 0x0c, 0x33, 0xce, 0x48, 0x7e, 0x33, 0xee, 0x55, 0x4d, 0x1c, 0xb9, 0x0a, 0x20, 0xfa, 0x96, 0x74, 0x49, 0x0e, 0xaf, + 0x64, 0xb9, 0xea, 0x0c, 0xf9, 0x57, 0x58, 0x67, 0xbd, 0x38, 0x01, 0x33, 0x69, 0xca, 0x4b, 0x4c, 0x4c, 0x11, 0x97, + 0x9b, 0x65, 0xcc, 0xd3, 0xf4, 0x59, 0xb4, 0x83, 0x93, 0x1b, 0x09, 0x1c, 0xb1, 0x6f, 0x2c, 0x43, 0x33, 0x61, 0x23, + 0x26, 0xd2, 0xa8, 0x94, 0x12, 0x3e, 0x90, 0x4b, 0x2d, 0xf9, 0xcb, 0x5c, 0x5e, 0x7d, 0xb9, 0x4d, 0x70, 0x40, 0x5e, + 0x03, 0xcb, 0xa1, 0x71, 0xdc, 0x32, 0x90, 0x88, 0xc5, 0x80, 0x18, 0xb5, 0x2a, 0x57, 0x93, 0x51, 0x9d, 0xcc, 0x57, + 0xc8, 0x85, 0x8a, 0x3c, 0xb8, 0x25, 0x50, 0xf2, 0xe7, 0x98, 0x3a, 0x98, 0x95, 0xda, 0x4d, 0x8b, 0x4d, 0x92, 0xf7, + 0xcc, 0x80, 0xe4, 0xfa, 0x6b, 0x78, 0x68, 0xfc, 0xe2, 0x95, 0x39, 0x25, 0x7c, 0x51, 0xc6, 0xd2, 0xd2, 0x98, 0x4b, + 0xff, 0x42, 0xde, 0xa7, 0x95, 0x80, 0xfd, 0x0a, 0x62, 0xca, 0xc0, 0x25, 0x36, 0x2e, 0x48, 0xca, 0x6b, 0x79, 0xca, + 0xee, 0x6b, 0x28, 0xdf, 0x15, 0x93, 0xae, 0x52, 0x59, 0x57, 0x58, 0x75, 0xbf, 0x2e, 0x58, 0x7e, 0xb1, 0xcf, 0x30, + 0x37, 0x19, 0x0d, 0xb2, 0x15, 0x33, 0x9b, 0xf2, 0xab, 0xbd, 0x6b, 0xbf, 0xf2, 0x50, 0xd2, 0xa1, 0x5a, 0xa5, 0x9b, + 0x57, 0x6e, 0x38, 0xc6, 0x8d, 0x1b, 0x8e, 0x00, 0x36, 0x86, 0x9d, 0x2a, 0x52, 0xeb, 0xfc, 0xf7, 0xd5, 0xf0, 0x13, + 0xed, 0xb5, 0xa1, 0xde, 0x75, 0xc3, 0xb5, 0xe9, 0xe9, 0xd7, 0xa0, 0x6a, 0x64, 0x09, 0x5d, 0x87, 0x2a, 0x26, 0x23, + 0x51, 0x62, 0xba, 0x4a, 0x79, 0xd4, 0xd7, 0x88, 0x73, 0x10, 0x37, 0x94, 0xbf, 0xf8, 0xe7, 0xf0, 0xe2, 0x28, 0x40, + 0x23, 0x6a, 0x39, 0xc9, 0x52, 0xde, 0x9a, 0x44, 0xb3, 0x38, 0xb9, 0x08, 0x16, 0x71, 0x6b, 0x96, 0xa5, 0x59, 0x31, + 0x07, 0xae, 0xf4, 0x8a, 0x0b, 0xb0, 0xe1, 0x67, 0xad, 0x45, 0xec, 0x3d, 0x67, 0xc9, 0x29, 0xe3, 0xf1, 0x28, 0xf2, + 0xec, 0xbd, 0x1c, 0xc4, 0x83, 0xf5, 0x3a, 0xca, 0xf3, 0xec, 0xcc, 0xf6, 0xde, 0x65, 0xc7, 0xc0, 0xb4, 0xde, 0x9b, + 0xf3, 0x8b, 0x13, 0x96, 0x7a, 0xef, 0x8f, 0x17, 0x29, 0x5f, 0x78, 0x45, 0x94, 0x16, 0xad, 0x82, 0xe5, 0xf1, 0x04, + 0xd4, 0x44, 0x92, 0xe5, 0x2d, 0xcc, 0x7f, 0x9e, 0xb1, 0x20, 0x89, 0x4f, 0xa6, 0xdc, 0x1a, 0x47, 0xf9, 0xa7, 0x5e, + 0xab, 0x35, 0xcf, 0xe3, 0x59, 0x94, 0x5f, 0xb4, 0xa8, 0x45, 0xf0, 0x45, 0x7b, 0x3b, 0xfa, 0x6a, 0x72, 0xbf, 0xc7, + 0x73, 0xe8, 0x1b, 0x23, 0x15, 0x03, 0x10, 0x3e, 0xd6, 0xf6, 0x4e, 0x7b, 0x56, 0xdc, 0x11, 0x27, 0x4a, 0x51, 0xca, + 0xcb, 0x23, 0xef, 0x23, 0x03, 0xb8, 0xfd, 0x63, 0x9e, 0x7a, 0xe0, 0xcb, 0xf1, 0x2c, 0x5d, 0x8e, 0x16, 0x79, 0x01, + 0x03, 0xcc, 0xb3, 0x38, 0xe5, 0x2c, 0xef, 0x1d, 0x67, 0x39, 0x90, 0xad, 0x95, 0x47, 0xe3, 0x78, 0x51, 0x04, 0xf7, + 0xe7, 0xe7, 0x3d, 0xb4, 0x15, 0x4e, 0xf2, 0x6c, 0x91, 0x8e, 0xe5, 0x5c, 0x71, 0x0a, 0x1b, 0x23, 0xe6, 0x66, 0x05, + 0x7d, 0x09, 0x05, 0xe0, 0x4b, 0x59, 0x94, 0xb7, 0x4e, 0xb0, 0x33, 0x1a, 0xfa, 0xed, 0x31, 0x3b, 0xf1, 0xf2, 0x93, + 0xe3, 0xc8, 0xe9, 0x74, 0x1f, 0x7a, 0xea, 0x9f, 0xbf, 0xe3, 0x82, 0xe1, 0xbe, 0xb6, 0xb8, 0xd3, 0x6e, 0xff, 0x9d, + 0xdb, 0x6b, 0xcc, 0x42, 0x00, 0x05, 0x9d, 0xf9, 0xb9, 0x55, 0x64, 0x09, 0xac, 0xcf, 0xba, 0x9e, 0xbd, 0x39, 0xf8, + 0x4d, 0x71, 0x7a, 0x12, 0x74, 0xe7, 0xe7, 0x25, 0x62, 0x17, 0x88, 0x84, 0x4c, 0x89, 0xa4, 0x7c, 0x5b, 0xfe, 0x5e, + 0x88, 0x1f, 0xad, 0x87, 0xb8, 0xab, 0x20, 0xae, 0xa8, 0xde, 0x1a, 0xc3, 0x3e, 0x20, 0xf2, 0x77, 0x0a, 0x01, 0xc8, + 0x14, 0x9c, 0xc0, 0x5c, 0xc1, 0x41, 0x2f, 0xbf, 0x1b, 0x8c, 0xee, 0x7a, 0x30, 0x1e, 0xdd, 0x04, 0x46, 0x9e, 0x8e, + 0x97, 0xf5, 0x75, 0xed, 0x80, 0x73, 0xda, 0x9b, 0x32, 0xe4, 0xa7, 0xa0, 0x8b, 0xcf, 0x67, 0xf1, 0x98, 0x4f, 0xc5, + 0x23, 0xb1, 0xf3, 0x99, 0xa8, 0xdb, 0x69, 0xb7, 0xc5, 0x7b, 0x01, 0x0a, 0x2d, 0xe8, 0xf8, 0xd8, 0x00, 0x98, 0xe8, + 0xc3, 0x55, 0x1f, 0xb1, 0xf9, 0xfa, 0xc6, 0x2f, 0xd5, 0x78, 0x67, 0x2a, 0x6f, 0x50, 0xa8, 0x08, 0xf5, 0xcd, 0x16, + 0xcc, 0x78, 0xcb, 0xfb, 0x1d, 0x7d, 0x50, 0x35, 0xf8, 0x9a, 0x91, 0xd6, 0x0b, 0xb8, 0x67, 0xe6, 0x02, 0xf5, 0xd2, + 0x3e, 0x86, 0xa4, 0x5a, 0x2d, 0x17, 0xf4, 0x06, 0xc3, 0x10, 0x12, 0x1d, 0x08, 0x3a, 0xf9, 0xa0, 0xa0, 0x6f, 0x6a, + 0x64, 0x6e, 0x50, 0x38, 0x99, 0x0b, 0x5b, 0x3e, 0xd3, 0x72, 0x1d, 0x94, 0x34, 0x78, 0xd9, 0x1f, 0x98, 0x6c, 0x00, + 0xd2, 0x9b, 0x42, 0x5d, 0x7f, 0x09, 0x85, 0x2b, 0xa5, 0x1c, 0xa9, 0xd9, 0x75, 0x57, 0xf4, 0x61, 0x55, 0x62, 0xca, + 0x48, 0x3e, 0x1c, 0xfe, 0x3b, 0x0c, 0x7b, 0x47, 0x3b, 0x96, 0x45, 0xb6, 0xc8, 0x47, 0x14, 0xa9, 0x5b, 0xf5, 0xf8, + 0x6d, 0x52, 0xb8, 0xb6, 0xc7, 0xb4, 0x9c, 0x47, 0x37, 0xb8, 0xf6, 0x91, 0x03, 0x4e, 0x87, 0x20, 0xe2, 0x8e, 0x81, + 0x8c, 0x72, 0x28, 0x08, 0x51, 0x75, 0x8d, 0x29, 0xdf, 0x8d, 0xee, 0x5f, 0xfa, 0x8b, 0x34, 0x06, 0x49, 0xf7, 0x31, + 0x1e, 0xd3, 0xbd, 0x93, 0x78, 0x4c, 0x07, 0x11, 0x2d, 0x4a, 0x3c, 0xc2, 0xc8, 0x36, 0x14, 0xa8, 0xef, 0xb0, 0xc0, + 0xb3, 0x4c, 0x64, 0xb1, 0x5b, 0x36, 0x1e, 0x26, 0x18, 0xaa, 0x72, 0x9c, 0xcd, 0xa2, 0x38, 0x0d, 0xf0, 0xfb, 0x20, + 0x9e, 0x1e, 0x31, 0xc0, 0x2e, 0x1e, 0xfc, 0x64, 0x32, 0x17, 0xad, 0xe3, 0xfa, 0xbf, 0x80, 0x1c, 0xa1, 0xfe, 0xa5, + 0xf4, 0xc3, 0x34, 0x5c, 0xea, 0x98, 0xb7, 0x5e, 0x0a, 0xb2, 0x87, 0x2b, 0x9b, 0x95, 0x51, 0x9c, 0x63, 0x97, 0xd3, + 0x8f, 0x41, 0xab, 0x13, 0x74, 0xb4, 0xeb, 0x5a, 0xbb, 0x8d, 0x2a, 0x72, 0x59, 0xe4, 0x8d, 0x46, 0x82, 0x41, 0x3f, + 0x0b, 0x38, 0xab, 0x77, 0x0d, 0xab, 0x27, 0xf9, 0x12, 0x03, 0x38, 0x27, 0xa9, 0x53, 0x03, 0x82, 0xce, 0x02, 0xae, + 0x98, 0xca, 0x2d, 0x23, 0x52, 0x4a, 0x8f, 0x69, 0x03, 0xd7, 0xef, 0x12, 0xe1, 0xbd, 0xa1, 0x7a, 0x0a, 0x94, 0x62, + 0xb9, 0xf1, 0xd1, 0xae, 0xd8, 0xf1, 0x16, 0xf1, 0x58, 0x68, 0xc3, 0x16, 0xb4, 0xad, 0x3f, 0x8d, 0x80, 0x4a, 0x9f, + 0x42, 0x7b, 0x63, 0xe9, 0xa8, 0xc4, 0xfa, 0x1c, 0xe6, 0xda, 0x13, 0x5a, 0x8f, 0x6e, 0xe4, 0xdb, 0xfd, 0x8d, 0x25, + 0x2f, 0x77, 0xb7, 0x44, 0xef, 0xfe, 0x51, 0x59, 0x90, 0x82, 0x32, 0x03, 0x69, 0xd5, 0x14, 0xa2, 0x0e, 0x86, 0xa5, + 0xf4, 0x5d, 0x1c, 0x37, 0xd7, 0x46, 0x97, 0x88, 0x18, 0x4b, 0xb6, 0x2b, 0x30, 0x5d, 0x29, 0xca, 0x61, 0x4f, 0xea, + 0x84, 0x94, 0x42, 0xe4, 0x60, 0xf4, 0x56, 0xa1, 0x38, 0x42, 0x08, 0x06, 0x1b, 0xcb, 0xb8, 0x0c, 0x37, 0x96, 0x59, + 0x79, 0x04, 0x96, 0x09, 0x42, 0x95, 0xab, 0xcf, 0xbb, 0xc0, 0xc4, 0x22, 0xc8, 0x62, 0xd1, 0x08, 0x38, 0x2d, 0x2b, + 0x6d, 0x6b, 0x20, 0xa0, 0x01, 0x0f, 0x10, 0x0b, 0xc0, 0x76, 0xa3, 0x5e, 0x0c, 0x70, 0x11, 0xad, 0xfb, 0x30, 0xd0, + 0xee, 0x96, 0x68, 0x04, 0x78, 0xe5, 0x08, 0x72, 0x85, 0x16, 0xa6, 0xe3, 0x98, 0xa8, 0x8d, 0xe3, 0x53, 0x4d, 0x3a, + 0xca, 0x4d, 0xee, 0xef, 0x26, 0xd1, 0x31, 0x4b, 0x60, 0xc8, 0xe2, 0xf2, 0xb2, 0x0d, 0x23, 0x89, 0x57, 0x6b, 0x37, + 0x4e, 0xe7, 0x0b, 0xf9, 0x99, 0x2d, 0x98, 0xb8, 0x83, 0x27, 0x9f, 0x78, 0x0b, 0x60, 0xa0, 0x8e, 0xf2, 0x02, 0x39, + 0x00, 0x80, 0x48, 0xa7, 0x08, 0x08, 0x5d, 0xc5, 0x16, 0x50, 0x1a, 0x8f, 0x57, 0xcb, 0x60, 0x27, 0xce, 0xb1, 0x34, + 0x85, 0xe7, 0x59, 0x9c, 0xe2, 0x63, 0x81, 0x8f, 0xd1, 0x39, 0x3e, 0x66, 0xf0, 0xa8, 0x71, 0xcf, 0x4b, 0xfb, 0x6f, + 0xb2, 0x02, 0x96, 0x26, 0x40, 0x76, 0x79, 0x09, 0xf2, 0x5e, 0x93, 0x60, 0x77, 0x0b, 0x88, 0x85, 0x9c, 0x22, 0x3e, + 0xba, 0xc2, 0x4c, 0x32, 0xb2, 0x62, 0xde, 0x12, 0xe5, 0x16, 0x69, 0xd5, 0x10, 0x9c, 0xae, 0xdc, 0x69, 0x18, 0x0f, + 0x9e, 0x4c, 0x2f, 0x79, 0x82, 0x2f, 0xae, 0x6d, 0x89, 0xaf, 0x62, 0x08, 0xa2, 0xd0, 0x23, 0x62, 0xa8, 0xcb, 0xb8, + 0xfc, 0xde, 0x4d, 0x1c, 0xda, 0x38, 0x0b, 0xd8, 0x6f, 0xa8, 0x05, 0x78, 0x14, 0x27, 0xa2, 0xf1, 0x1a, 0x7c, 0x1a, + 0x79, 0x82, 0x84, 0xce, 0xee, 0x56, 0x05, 0x1b, 0x00, 0x3f, 0x12, 0xb7, 0xac, 0x1d, 0x91, 0x03, 0x69, 0x8b, 0x72, + 0x3a, 0x3b, 0x97, 0x5b, 0x5a, 0x46, 0x76, 0x45, 0xac, 0x5c, 0xa3, 0x4a, 0x39, 0x8b, 0xf6, 0x24, 0x4a, 0xd7, 0x35, + 0x05, 0xe8, 0xe7, 0x8c, 0x8d, 0x3d, 0xdb, 0x02, 0x59, 0x2a, 0x9e, 0x3f, 0x26, 0xec, 0x94, 0xc9, 0x2f, 0xa5, 0xe8, + 0x41, 0x74, 0xe5, 0x08, 0x54, 0x32, 0x97, 0x97, 0x38, 0x25, 0x7b, 0x2a, 0x1c, 0x25, 0x25, 0xea, 0x88, 0x78, 0xb6, + 0x31, 0x68, 0x73, 0x8e, 0x76, 0x7d, 0x58, 0xaf, 0x03, 0xd6, 0xae, 0x2d, 0xe0, 0x25, 0x3b, 0xee, 0x66, 0xe4, 0x60, + 0x00, 0x36, 0x99, 0xc0, 0x76, 0x51, 0x91, 0x65, 0x2d, 0x0b, 0x04, 0x54, 0xe0, 0x94, 0x7a, 0xb6, 0x68, 0x61, 0x57, + 0x6d, 0xf5, 0x93, 0x24, 0x4e, 0x92, 0x8d, 0x3e, 0xad, 0x99, 0x0b, 0xb8, 0x63, 0x43, 0x44, 0x5a, 0x1b, 0xf2, 0xcd, + 0xfe, 0xb7, 0x7f, 0xfe, 0x9f, 0xff, 0x15, 0x06, 0xa6, 0x7e, 0x6e, 0x69, 0x5d, 0xdd, 0xea, 0x7f, 0x40, 0xab, 0x45, + 0x7a, 0x43, 0xbb, 0xbf, 0xfe, 0xe3, 0x7f, 0x83, 0x66, 0x74, 0x23, 0xc7, 0x2d, 0x8f, 0x08, 0xa2, 0x11, 0x1a, 0x41, + 0x9f, 0x05, 0x52, 0x6d, 0x90, 0x2b, 0x67, 0xfa, 0x27, 0x04, 0xbb, 0xe0, 0xd9, 0xfc, 0x5a, 0x70, 0x10, 0xea, 0x51, + 0x92, 0x15, 0x4c, 0xc3, 0x23, 0x64, 0xed, 0xe7, 0x01, 0x44, 0x73, 0xcd, 0x81, 0xcb, 0x0b, 0x4b, 0x8f, 0x23, 0x96, + 0x67, 0xdd, 0x38, 0x8d, 0xd5, 0x2b, 0x18, 0x27, 0x74, 0x28, 0xae, 0x00, 0xeb, 0x25, 0x9e, 0xe0, 0x81, 0x04, 0x82, + 0x5b, 0xff, 0xca, 0xd7, 0xfa, 0xc1, 0x34, 0x7f, 0x8a, 0xb1, 0x44, 0x28, 0x45, 0x8d, 0x00, 0x3f, 0x41, 0x68, 0x7d, + 0xd4, 0xcf, 0xd1, 0xb9, 0x7e, 0x46, 0xc1, 0x26, 0x26, 0x00, 0x5d, 0x34, 0x43, 0x33, 0xc3, 0x9c, 0x41, 0xa4, 0x01, + 0x54, 0x7e, 0xa4, 0x91, 0x4d, 0x22, 0x84, 0xd7, 0x47, 0x4c, 0xba, 0xc4, 0x2b, 0x36, 0x8b, 0x9c, 0x7d, 0x4c, 0xb2, + 0x33, 0x0c, 0x4e, 0x21, 0x91, 0xae, 0xaa, 0x2f, 0xff, 0xf5, 0x5f, 0x7c, 0xff, 0x5f, 0xff, 0xe5, 0x8a, 0x06, 0x53, + 0xd8, 0x07, 0x60, 0x4d, 0xf2, 0x50, 0xd3, 0xb9, 0x81, 0xd6, 0xfa, 0x41, 0x11, 0xcf, 0xf5, 0x35, 0x12, 0x71, 0x2c, + 0x95, 0x78, 0xcb, 0x47, 0x42, 0x5b, 0x33, 0xc5, 0xcd, 0xb3, 0x20, 0x64, 0x57, 0x4c, 0x83, 0x55, 0x37, 0xcc, 0x73, + 0xe4, 0x06, 0xd7, 0xd0, 0xe5, 0x33, 0x31, 0x5e, 0x0f, 0xc6, 0x8d, 0x10, 0x78, 0x20, 0xfd, 0x85, 0x7e, 0x78, 0x22, + 0xa4, 0x7b, 0x20, 0x96, 0x41, 0xca, 0xfa, 0x1a, 0x40, 0x9e, 0x75, 0x40, 0x13, 0x50, 0x93, 0xb8, 0xd2, 0xad, 0x64, + 0x8a, 0x1c, 0xe7, 0xfd, 0x57, 0x78, 0xab, 0xce, 0xc2, 0xde, 0xa8, 0x59, 0x0b, 0x32, 0x04, 0x38, 0x19, 0x02, 0x52, + 0x03, 0x99, 0x3a, 0x18, 0x3d, 0x8c, 0x6c, 0xbd, 0xae, 0xfd, 0x88, 0xdd, 0x6b, 0xda, 0x92, 0x4b, 0x6d, 0x19, 0x4b, + 0x4b, 0x56, 0x6a, 0x4b, 0xfc, 0x76, 0x4a, 0x43, 0x5b, 0xc6, 0x57, 0x6a, 0x4b, 0xa4, 0xdc, 0x00, 0x47, 0x0e, 0xed, + 0x4d, 0x0c, 0xa3, 0x18, 0xba, 0x99, 0xa3, 0x5d, 0x02, 0xbe, 0xf3, 0xe8, 0x93, 0x34, 0x4b, 0x08, 0x01, 0x8c, 0x23, + 0x68, 0x43, 0x4b, 0x60, 0x00, 0x2a, 0xf6, 0xa8, 0xd4, 0x9b, 0x1e, 0x1f, 0x8d, 0x09, 0xb8, 0xbb, 0x9c, 0x30, 0x14, + 0xc9, 0x47, 0x5b, 0x38, 0x84, 0xd8, 0x2b, 0x25, 0x3d, 0x03, 0x52, 0x5b, 0x38, 0xce, 0x91, 0xb7, 0x14, 0x01, 0xa9, + 0xc0, 0x7e, 0xfb, 0x66, 0xff, 0xc0, 0xf6, 0x8e, 0xb3, 0xf1, 0x45, 0x60, 0x83, 0x33, 0x01, 0x86, 0x87, 0xeb, 0xf3, + 0x29, 0x4b, 0x1d, 0x65, 0xce, 0x67, 0x09, 0xb8, 0x33, 0xd9, 0x89, 0xf8, 0x7e, 0x42, 0x33, 0x98, 0x0e, 0x34, 0xa5, + 0x0f, 0x2c, 0xf6, 0x77, 0xb9, 0xf8, 0xf6, 0x28, 0xcf, 0xf1, 0xb1, 0x8f, 0xe9, 0x04, 0xbb, 0x5b, 0xf0, 0x80, 0x2f, + 0xfb, 0xa8, 0x8a, 0xf4, 0x9b, 0x80, 0xb3, 0x10, 0xef, 0x5b, 0xd8, 0x7e, 0x4b, 0xf5, 0x45, 0x28, 0xfa, 0x92, 0xd1, + 0xb4, 0xc5, 0x5d, 0x99, 0x71, 0x34, 0xf6, 0x18, 0xad, 0x34, 0xb2, 0xb8, 0x81, 0x1a, 0x7c, 0xac, 0x4b, 0x84, 0xe6, + 0x37, 0x8a, 0x68, 0x94, 0x4a, 0x4f, 0xcb, 0x2a, 0x9c, 0x90, 0x2c, 0x3b, 0x31, 0x19, 0xfc, 0x24, 0xf0, 0x8f, 0xcc, + 0x6f, 0x85, 0x89, 0xcf, 0xfa, 0x68, 0x24, 0x0f, 0xff, 0xec, 0x7d, 0x64, 0xde, 0xc5, 0x11, 0xb5, 0x54, 0x8e, 0x29, + 0x46, 0x4c, 0xd0, 0x81, 0x6f, 0xab, 0x08, 0x04, 0x98, 0x26, 0x49, 0x34, 0x2f, 0x58, 0xa0, 0x1e, 0xa4, 0x8f, 0x8a, + 0xae, 0xee, 0x6a, 0x50, 0xc0, 0x34, 0x61, 0x4a, 0x3e, 0x5d, 0x9a, 0x4e, 0xec, 0x03, 0x70, 0x62, 0x31, 0x01, 0xbf, + 0x15, 0x81, 0xe2, 0x4d, 0x83, 0x84, 0x4d, 0x78, 0xc9, 0xf1, 0x86, 0xf7, 0x52, 0x45, 0x0d, 0xfc, 0xee, 0x0e, 0x38, + 0xb6, 0x96, 0x8f, 0xff, 0xdf, 0x34, 0xf6, 0x38, 0x48, 0xc1, 0x11, 0xa5, 0x2b, 0x1f, 0x78, 0x9d, 0x0e, 0x20, 0x32, + 0xdf, 0x97, 0xc6, 0x44, 0x23, 0x86, 0x11, 0x95, 0x92, 0xe7, 0x20, 0xb2, 0x3d, 0x9e, 0x9b, 0xed, 0x40, 0xd4, 0xae, + 0x84, 0x55, 0x56, 0x1d, 0xfb, 0x6d, 0x57, 0x9a, 0xff, 0xab, 0x8d, 0x55, 0x74, 0xa4, 0xfe, 0xb6, 0x42, 0x21, 0x23, + 0x8e, 0x53, 0x0a, 0x2d, 0xb3, 0x14, 0x3d, 0x4c, 0x9c, 0x56, 0x23, 0x3c, 0x37, 0x1a, 0x89, 0x25, 0xed, 0xf8, 0x43, + 0xda, 0xf1, 0x24, 0xc1, 0x86, 0x4b, 0x31, 0xf7, 0x28, 0x4a, 0x46, 0x0e, 0x02, 0x60, 0xb5, 0xac, 0x47, 0x40, 0x4d, + 0x57, 0x45, 0x19, 0xfc, 0x87, 0x48, 0xdc, 0x52, 0xc8, 0xbb, 0x35, 0x54, 0x3a, 0x1a, 0x96, 0x65, 0xef, 0x8c, 0x39, + 0x87, 0xbf, 0xc9, 0x0b, 0x03, 0xe2, 0x9e, 0xa8, 0xfe, 0xd6, 0x5e, 0xbb, 0x74, 0x87, 0xde, 0x5f, 0x8c, 0x0f, 0x98, + 0xd9, 0x8a, 0xa1, 0x6d, 0x0f, 0x96, 0xe1, 0x2f, 0x21, 0xf6, 0x7d, 0xe5, 0xd8, 0x68, 0x55, 0x52, 0xcd, 0x45, 0x8b, + 0xf8, 0xcb, 0xc6, 0x6e, 0x22, 0xdc, 0xfd, 0xfd, 0x55, 0x51, 0x8b, 0x6f, 0x6e, 0x8e, 0x5a, 0xb0, 0x5b, 0x46, 0x2d, + 0xbe, 0xf9, 0x83, 0xa3, 0x16, 0xdf, 0x37, 0xa3, 0x16, 0xbf, 0x7e, 0x4e, 0xd4, 0x22, 0xcf, 0xce, 0x8a, 0xb0, 0x23, + 0x4f, 0xc9, 0x41, 0xe6, 0xfc, 0x6d, 0xc2, 0x17, 0x30, 0x51, 0x23, 0x78, 0x41, 0xd1, 0x0a, 0x91, 0xd8, 0x07, 0x92, + 0x5d, 0xc6, 0x0a, 0xda, 0x3a, 0x83, 0xae, 0x75, 0x5f, 0x5d, 0x19, 0x02, 0x6f, 0xcd, 0xd5, 0x97, 0xdd, 0xba, 0x2a, + 0x9a, 0x10, 0xd0, 0x37, 0x3f, 0x75, 0xc7, 0xee, 0xa6, 0x4a, 0xdd, 0x32, 0x47, 0xe8, 0xa9, 0x88, 0xbc, 0x60, 0x9f, + 0xa5, 0xfd, 0x9f, 0x0e, 0x3b, 0xbd, 0xed, 0xce, 0x0c, 0x7a, 0x83, 0x0e, 0x85, 0xb7, 0x76, 0x6f, 0x7b, 0x1b, 0xdf, + 0xce, 0xd4, 0x5b, 0x17, 0xdf, 0x62, 0xf5, 0xb6, 0x83, 0x6f, 0x23, 0xf5, 0xf6, 0x00, 0xdf, 0xc6, 0xea, 0xed, 0x21, + 0xbe, 0x9d, 0xda, 0xe5, 0x21, 0xd7, 0xc0, 0x3d, 0x04, 0xbe, 0x22, 0x43, 0x3f, 0x50, 0x65, 0xb0, 0x69, 0xf1, 0xaa, + 0x5d, 0x74, 0x12, 0xc4, 0x9e, 0x70, 0x88, 0x82, 0xdc, 0x3b, 0x03, 0xc9, 0x1f, 0x50, 0x66, 0xd9, 0x53, 0xfc, 0xe6, + 0x02, 0xf8, 0x0f, 0x07, 0xf1, 0x8c, 0xa9, 0x8f, 0xcf, 0x2a, 0xac, 0xc1, 0x86, 0x3c, 0x6c, 0x0f, 0xcb, 0x9e, 0x5e, + 0x27, 0x11, 0x2c, 0x51, 0x27, 0xf7, 0xb4, 0x72, 0x55, 0x9d, 0x98, 0xae, 0xa5, 0x57, 0xf8, 0x0a, 0x3d, 0x62, 0xb8, + 0xd0, 0x13, 0xb0, 0x8d, 0x5a, 0xe7, 0xe0, 0x74, 0xad, 0xd5, 0x2d, 0x08, 0x91, 0xd6, 0x26, 0x84, 0x93, 0x7e, 0x3b, + 0x88, 0x4e, 0xf4, 0xf3, 0x2b, 0x30, 0x76, 0xa3, 0x13, 0x76, 0x93, 0x9e, 0x21, 0x10, 0x4d, 0x1d, 0xa3, 0x80, 0x20, + 0x6b, 0x08, 0x96, 0x06, 0x9d, 0x3f, 0xa9, 0x63, 0x90, 0x3a, 0x75, 0xad, 0x43, 0xd3, 0xd7, 0x8b, 0x80, 0xa2, 0x55, + 0xc1, 0x2e, 0xd8, 0xdc, 0x54, 0x2a, 0x28, 0x0c, 0x15, 0x58, 0x70, 0xad, 0x2a, 0xd2, 0xfe, 0xf1, 0x95, 0x0a, 0xc9, + 0x52, 0xba, 0xc8, 0x8c, 0xe4, 0xeb, 0x30, 0xfe, 0xaa, 0x78, 0xfc, 0xa2, 0x33, 0xc2, 0x3f, 0x52, 0xf8, 0x7e, 0x31, + 0x99, 0x4c, 0xae, 0xd5, 0x4d, 0x5f, 0x8c, 0x27, 0xac, 0xcb, 0x76, 0x7a, 0x18, 0xe5, 0x6d, 0x49, 0x71, 0xd8, 0x29, + 0x89, 0x76, 0xcb, 0xdb, 0x35, 0x46, 0xc9, 0x09, 0xea, 0xea, 0xf6, 0x4a, 0xac, 0x04, 0xaa, 0x2c, 0x41, 0x78, 0x9f, + 0xc4, 0x69, 0xd0, 0x2e, 0xfd, 0x53, 0x29, 0xf5, 0xbf, 0x78, 0xf4, 0xe8, 0x51, 0xe9, 0x8f, 0xd5, 0x5b, 0x7b, 0x3c, + 0x2e, 0xfd, 0xd1, 0x52, 0xa3, 0xd1, 0x6e, 0x4f, 0x26, 0xa5, 0x1f, 0xab, 0x82, 0xed, 0xee, 0x68, 0xbc, 0xdd, 0x2d, + 0xfd, 0x33, 0xa3, 0x45, 0xe9, 0x33, 0xf9, 0x96, 0xb3, 0x71, 0x2d, 0x54, 0xfc, 0xb0, 0x0d, 0x95, 0x82, 0xd1, 0x96, + 0xe8, 0xe0, 0x89, 0xc7, 0x20, 0x5a, 0xf0, 0x0c, 0x6c, 0xab, 0xb2, 0xc7, 0x40, 0x3e, 0x4f, 0xa4, 0x6c, 0x17, 0xdf, + 0x76, 0x45, 0x89, 0xfe, 0xab, 0x29, 0xd1, 0x91, 0x99, 0x49, 0x9a, 0x33, 0xd2, 0x03, 0xcd, 0x6a, 0xe4, 0x2c, 0xaa, + 0xfe, 0x35, 0x64, 0x95, 0xb0, 0x47, 0x69, 0x83, 0x2d, 0x85, 0x8c, 0xff, 0xf6, 0x2a, 0x19, 0xff, 0xdd, 0xcd, 0x32, + 0xfe, 0xf8, 0x76, 0x22, 0xfe, 0xbb, 0x3f, 0x58, 0xc4, 0x7f, 0x6b, 0x8a, 0x78, 0x21, 0xc4, 0x2e, 0xc0, 0x7a, 0x25, + 0xb3, 0xf5, 0x38, 0x3b, 0x6f, 0xe1, 0x96, 0xc8, 0x6d, 0x92, 0x9e, 0x1b, 0xb7, 0x12, 0xfe, 0x6b, 0x72, 0x7f, 0xd4, + 0x60, 0xc6, 0x87, 0x62, 0x79, 0x76, 0x72, 0x92, 0x30, 0x25, 0xe3, 0x8d, 0x0a, 0xb2, 0x88, 0xdf, 0xa4, 0xa1, 0xfd, + 0x06, 0x9c, 0x53, 0xa3, 0x64, 0x32, 0x81, 0xa2, 0xc9, 0xc4, 0x56, 0xb9, 0xb1, 0x20, 0xcf, 0xa8, 0xd5, 0xeb, 0x5a, + 0x09, 0xb5, 0xfa, 0xfa, 0x6b, 0xb3, 0xcc, 0x2c, 0x90, 0x51, 0x28, 0xd3, 0x98, 0x90, 0x35, 0xe3, 0xb8, 0xc0, 0x3d, + 0x58, 0x7d, 0xd8, 0x16, 0xed, 0x95, 0x19, 0x28, 0x95, 0x78, 0x84, 0x5f, 0x4c, 0x69, 0x7e, 0x44, 0x44, 0xe4, 0x31, + 0xaf, 0x22, 0x57, 0x9d, 0x75, 0x1a, 0xdf, 0xab, 0xab, 0xce, 0x37, 0x61, 0xf1, 0x65, 0x2e, 0xc3, 0xe3, 0x8b, 0x17, + 0x63, 0xe7, 0x02, 0xec, 0xd8, 0xb8, 0x78, 0x93, 0x36, 0x72, 0xc4, 0x04, 0xd8, 0x61, 0x68, 0x62, 0x5a, 0x0a, 0x82, + 0x55, 0xc9, 0xf2, 0x55, 0x65, 0xcf, 0xe8, 0x24, 0x53, 0x89, 0x70, 0xc8, 0x41, 0x8d, 0x2c, 0x81, 0x39, 0x98, 0xd4, + 0x85, 0xf4, 0xe1, 0x72, 0x91, 0x60, 0x71, 0x2a, 0xbf, 0x70, 0x4d, 0x91, 0xff, 0xa5, 0xd4, 0x1f, 0xf2, 0xe8, 0xbd, + 0xea, 0x89, 0xc1, 0x76, 0x31, 0xc3, 0xb8, 0x54, 0x01, 0x76, 0x20, 0xdc, 0x1c, 0x3f, 0xc7, 0x23, 0x86, 0x50, 0x71, + 0xec, 0x8a, 0x7a, 0xf8, 0xf9, 0x93, 0xea, 0xab, 0x90, 0xb5, 0x2f, 0x08, 0x36, 0x78, 0x00, 0xbf, 0xec, 0xcf, 0x51, + 0x1b, 0x64, 0x0b, 0xee, 0x38, 0xd4, 0xca, 0x71, 0x4b, 0xaf, 0xbb, 0xd3, 0x06, 0x15, 0xe3, 0x8b, 0x6f, 0xfe, 0x38, + 0xba, 0xb3, 0xc4, 0xf7, 0xaa, 0xb0, 0xf9, 0xca, 0x37, 0xb8, 0x34, 0x89, 0xf1, 0x23, 0x21, 0x02, 0x51, 0xe3, 0x9e, + 0x88, 0x5a, 0xc4, 0xe6, 0xbb, 0xaf, 0xdc, 0x37, 0x83, 0xb0, 0xee, 0x3a, 0x0e, 0x96, 0x31, 0xb2, 0x7a, 0x21, 0xb6, + 0x15, 0x56, 0xcd, 0x2a, 0x38, 0x37, 0xe8, 0xcc, 0xe2, 0xcc, 0x88, 0x39, 0xd7, 0xb6, 0x41, 0xa9, 0x82, 0xce, 0x22, + 0x72, 0x7c, 0x81, 0xf1, 0x51, 0xe1, 0xfb, 0x2a, 0xa0, 0xeb, 0x5e, 0xa7, 0x01, 0x39, 0xfa, 0xa3, 0x9a, 0xd1, 0x55, + 0x95, 0x2a, 0x28, 0xcd, 0x13, 0x02, 0x03, 0x19, 0x0a, 0xfe, 0xc2, 0x1a, 0xa7, 0x42, 0x6f, 0xc1, 0x34, 0x24, 0x80, + 0xb5, 0x47, 0x86, 0x6e, 0x89, 0xad, 0xc0, 0x16, 0xd2, 0x02, 0x94, 0x1e, 0x76, 0xe8, 0x5b, 0x35, 0xd0, 0xd3, 0xd5, + 0x98, 0xf1, 0x75, 0x4e, 0xda, 0xc5, 0x91, 0x5f, 0x9c, 0x79, 0xf0, 0xcf, 0xfa, 0x72, 0x09, 0x52, 0xfe, 0xf8, 0x53, + 0xcc, 0xc1, 0xa6, 0x9e, 0xb7, 0x30, 0x02, 0x42, 0x21, 0x4c, 0xa9, 0x0e, 0xe9, 0xd8, 0x51, 0x5c, 0x0f, 0xea, 0x2d, + 0x0a, 0xf4, 0xe5, 0xc8, 0x69, 0x09, 0xd2, 0x2c, 0x65, 0xbd, 0xfa, 0xf1, 0xb2, 0xe9, 0x37, 0x28, 0x62, 0x0d, 0x97, + 0x19, 0xfa, 0x7e, 0xfc, 0x02, 0x7c, 0x3f, 0xa1, 0x46, 0xdb, 0xca, 0x69, 0x68, 0xaf, 0x6d, 0x1f, 0x48, 0xda, 0x6e, + 0x92, 0xb5, 0x90, 0xaf, 0x3a, 0x47, 0x57, 0x39, 0x37, 0x37, 0x1d, 0xb6, 0x76, 0x77, 0x76, 0x3c, 0xf5, 0xcf, 0x38, + 0xa5, 0x6e, 0x16, 0xd3, 0x61, 0xeb, 0x6d, 0x20, 0x0b, 0xa2, 0x09, 0x7e, 0x4d, 0xef, 0x36, 0x2d, 0x8f, 0x29, 0xd3, + 0x71, 0x89, 0x6a, 0x3d, 0xe8, 0x3c, 0x02, 0x6f, 0xed, 0xd6, 0xc3, 0x5f, 0x8f, 0x7e, 0x29, 0x69, 0xa4, 0x2e, 0xac, + 0xda, 0x76, 0x0f, 0xe5, 0x45, 0x12, 0x5d, 0x80, 0xd3, 0x48, 0x36, 0xc6, 0x31, 0x06, 0x70, 0x7b, 0xf3, 0x4c, 0x66, + 0x0d, 0xe4, 0x2c, 0xa1, 0x5f, 0x59, 0x21, 0x97, 0x62, 0xfb, 0xc1, 0xfc, 0x5c, 0xad, 0x46, 0xa7, 0x91, 0x0d, 0xf0, + 0x87, 0x1e, 0xfa, 0x5f, 0x9d, 0x65, 0x50, 0x3f, 0xb8, 0xde, 0x01, 0x18, 0x84, 0x61, 0xd3, 0xca, 0x05, 0x54, 0x6d, + 0x28, 0x31, 0xd2, 0x1e, 0xaa, 0x81, 0x2c, 0x7f, 0x1b, 0x54, 0x65, 0x54, 0xb0, 0x1e, 0x7e, 0xd6, 0x30, 0x06, 0xd7, + 0x54, 0x1a, 0x4f, 0xb3, 0x78, 0x3c, 0x4e, 0x58, 0x4f, 0xd9, 0x47, 0x56, 0xe7, 0x01, 0x66, 0x0d, 0x98, 0x4b, 0x56, + 0x5f, 0x15, 0x83, 0x78, 0x9a, 0x4e, 0xd1, 0x31, 0xd8, 0x6b, 0xf8, 0x6d, 0xc2, 0xb5, 0xe4, 0x94, 0xc7, 0xe9, 0xed, + 0x8a, 0x78, 0xf4, 0x5c, 0xc7, 0x65, 0x07, 0x8c, 0x45, 0x5a, 0xf0, 0x76, 0x8f, 0x67, 0xf3, 0xa0, 0xb5, 0x5d, 0x47, + 0x04, 0xab, 0x34, 0x0a, 0xde, 0x1a, 0xb4, 0x3c, 0xb4, 0x0e, 0x84, 0x96, 0xb3, 0xfc, 0x8e, 0x2c, 0xa3, 0x01, 0xf0, + 0xfb, 0x77, 0xba, 0xa8, 0xac, 0x23, 0xf3, 0xff, 0x67, 0xb7, 0x7c, 0xb5, 0x7e, 0xb7, 0x7c, 0xa5, 0x76, 0xcb, 0xf5, + 0x1c, 0xfb, 0xc5, 0xa4, 0x83, 0x7f, 0x7a, 0x15, 0x42, 0xb0, 0x2a, 0x40, 0x0e, 0x0b, 0xed, 0xe2, 0x56, 0x17, 0xfe, + 0xa3, 0xa1, 0xdb, 0x1e, 0xfe, 0xf1, 0xc1, 0x02, 0x6c, 0x5b, 0x58, 0x88, 0xff, 0xda, 0xb5, 0xaa, 0xce, 0x7d, 0xac, + 0xc3, 0x5e, 0x3b, 0xab, 0x75, 0xdd, 0xeb, 0x37, 0x2d, 0xc8, 0x2b, 0xee, 0x04, 0x4a, 0x18, 0x83, 0xab, 0x16, 0x1d, + 0x1f, 0x43, 0xe9, 0x24, 0x1b, 0x2d, 0x8a, 0xbf, 0x97, 0xf0, 0x4b, 0x22, 0x5e, 0xbb, 0xa5, 0x1b, 0xe3, 0xa8, 0xae, + 0x22, 0x05, 0x45, 0x8d, 0xb0, 0xd4, 0xeb, 0x14, 0x14, 0xc0, 0x98, 0xcc, 0xe9, 0xfa, 0xf7, 0xd7, 0x6c, 0x82, 0xbf, + 0xc9, 0xda, 0xac, 0x45, 0xe6, 0xdf, 0x4b, 0x8c, 0x6b, 0x89, 0xf0, 0x59, 0x34, 0x30, 0xd7, 0xb0, 0xfd, 0x68, 0x3d, + 0xb8, 0x87, 0x6a, 0xa6, 0xa1, 0x52, 0x0a, 0x52, 0xef, 0x80, 0x17, 0x10, 0x2d, 0x12, 0x7e, 0xfd, 0xa8, 0x57, 0x71, + 0xc6, 0xca, 0xa8, 0xd7, 0x08, 0xf4, 0xaa, 0xed, 0x2d, 0xa5, 0xf4, 0x17, 0x5f, 0xdd, 0xc7, 0x3f, 0x22, 0xf0, 0x75, + 0x5c, 0xf9, 0x46, 0x22, 0x36, 0x80, 0xbe, 0xd1, 0x46, 0xcd, 0xf9, 0x11, 0x1a, 0x9c, 0xfc, 0x9f, 0xdb, 0xb6, 0x46, + 0x63, 0xfd, 0x56, 0xcd, 0xa5, 0x55, 0xfa, 0x59, 0xad, 0x3f, 0x6f, 0xf0, 0x5b, 0xb6, 0x1d, 0x09, 0x87, 0xa0, 0xde, + 0x56, 0xfe, 0xfa, 0x90, 0x95, 0xc6, 0x8a, 0xe2, 0xb7, 0x6d, 0x5f, 0x99, 0xc4, 0xd4, 0x63, 0x23, 0x3c, 0xd6, 0x4e, + 0xa4, 0x3c, 0x6f, 0xc6, 0x1e, 0xc2, 0x8f, 0xfc, 0x91, 0x85, 0xf7, 0xf0, 0xcb, 0x5b, 0xd6, 0xf9, 0x2c, 0x49, 0xc1, + 0xac, 0x9a, 0x72, 0x3e, 0x0f, 0xb6, 0xb6, 0xce, 0xce, 0xce, 0xfc, 0xb3, 0x6d, 0x3f, 0xcb, 0x4f, 0xb6, 0xba, 0xed, + 0x76, 0x1b, 0x3f, 0x98, 0x64, 0x5b, 0xa7, 0x31, 0x3b, 0x7b, 0x0c, 0xee, 0x87, 0xfd, 0xd0, 0x7a, 0x64, 0x3d, 0xdc, + 0xb6, 0x76, 0x1e, 0xd8, 0x16, 0x29, 0x00, 0x28, 0xd9, 0xb6, 0x2d, 0xa1, 0x00, 0x42, 0x1b, 0x8a, 0xfb, 0xbb, 0x27, + 0xca, 0x86, 0xc3, 0x84, 0x74, 0x61, 0x21, 0x81, 0xff, 0x96, 0x7d, 0x62, 0xf5, 0xad, 0x2e, 0xca, 0x5a, 0x52, 0x8d, + 0xa8, 0x57, 0xdc, 0xef, 0xa3, 0x68, 0x1e, 0x10, 0x1b, 0x99, 0x85, 0x18, 0x26, 0x13, 0xa5, 0x34, 0x05, 0xda, 0xa5, + 0xc7, 0xf0, 0x04, 0x6e, 0xc1, 0xd4, 0x82, 0xe7, 0x57, 0xdd, 0x87, 0xa0, 0xe3, 0x4e, 0x5b, 0xf7, 0x47, 0xed, 0x56, + 0xc7, 0xea, 0xb4, 0xba, 0xfe, 0x43, 0xab, 0x2b, 0xfe, 0x07, 0x19, 0xb9, 0x6d, 0x75, 0xe0, 0x69, 0xdb, 0x82, 0xf7, + 0xd3, 0xfb, 0x22, 0x1d, 0x22, 0xb2, 0xb7, 0xfa, 0xbb, 0xf8, 0xfb, 0x83, 0x00, 0xa9, 0xaf, 0x6c, 0xf1, 0x1b, 0xcf, + 0xec, 0x2f, 0xcc, 0xd2, 0xce, 0xa3, 0xb5, 0xc5, 0xdd, 0x87, 0x6b, 0x8b, 0xb7, 0x1f, 0xac, 0x2d, 0xbe, 0xbf, 0x53, + 0x2f, 0xde, 0x3a, 0x11, 0x55, 0x5a, 0x2e, 0x84, 0xf6, 0x2c, 0x02, 0x46, 0x39, 0x77, 0x3a, 0x00, 0x67, 0xdb, 0x6a, + 0xe1, 0x8f, 0x87, 0x5d, 0x57, 0xf7, 0x3a, 0xc6, 0x5e, 0x1a, 0xcb, 0x87, 0x8f, 0x00, 0xcb, 0xe7, 0xdd, 0x07, 0x23, + 0x6c, 0x47, 0x88, 0xc2, 0xbf, 0xd3, 0xed, 0x47, 0x23, 0xd0, 0x08, 0x16, 0xfe, 0x83, 0x3f, 0xd3, 0x9d, 0xee, 0x48, + 0xbc, 0xb4, 0xb1, 0xfe, 0x43, 0xe7, 0x61, 0x01, 0x4d, 0xf1, 0xcf, 0x6f, 0xda, 0x84, 0x46, 0x03, 0xde, 0x1c, 0xf7, + 0x3e, 0xd0, 0xe8, 0xd1, 0xb4, 0xeb, 0x7f, 0x75, 0xfa, 0xd0, 0x7f, 0x34, 0xed, 0x3c, 0xfc, 0x20, 0xde, 0x12, 0xa0, + 0xe0, 0x57, 0xf8, 0xef, 0xc3, 0x76, 0x7b, 0xda, 0xea, 0xf8, 0x8f, 0x4e, 0xb7, 0xfd, 0xed, 0xa4, 0xf5, 0xc0, 0x7f, + 0x84, 0xff, 0xaa, 0xe1, 0xa6, 0xd9, 0x8c, 0xd9, 0x16, 0xae, 0x77, 0xc3, 0xef, 0x35, 0xe7, 0xe8, 0xde, 0xb7, 0x76, + 0xee, 0x3f, 0x7f, 0x04, 0x6b, 0x34, 0xed, 0x74, 0xe1, 0xff, 0xab, 0x1e, 0x3f, 0x20, 0xe1, 0xe5, 0xc0, 0x11, 0xc3, + 0x54, 0x52, 0x45, 0x38, 0xfa, 0x78, 0xd7, 0x3d, 0xef, 0x87, 0xab, 0x02, 0x20, 0x7f, 0xbe, 0x39, 0x00, 0xf2, 0x97, + 0x5b, 0x06, 0xb9, 0xff, 0xfc, 0x07, 0x47, 0x40, 0x7e, 0x68, 0x06, 0xb9, 0xf7, 0xd8, 0x4a, 0xa0, 0xa3, 0xe9, 0xac, + 0x3d, 0x67, 0xce, 0xe1, 0x8f, 0x6c, 0x88, 0x69, 0xd3, 0xd0, 0xfa, 0x2f, 0xb5, 0x78, 0x50, 0x86, 0x1b, 0x79, 0x8f, + 0x89, 0x9d, 0xcc, 0xf8, 0x15, 0x04, 0xe1, 0xfc, 0x46, 0x82, 0xbc, 0xb8, 0x1d, 0x3d, 0x38, 0xff, 0x63, 0xe9, 0x41, + 0x5f, 0xee, 0x57, 0xf4, 0xa8, 0x45, 0xdc, 0x29, 0x62, 0x40, 0x8e, 0xfe, 0x3e, 0xbd, 0x3b, 0xf6, 0x16, 0xc3, 0xb7, + 0xc2, 0x16, 0xb9, 0x80, 0xef, 0x3e, 0xe7, 0x74, 0x40, 0x64, 0x15, 0x87, 0xb6, 0x0c, 0xc0, 0xcc, 0xf1, 0xdb, 0xb4, + 0xea, 0xe5, 0x54, 0xdc, 0x5c, 0x09, 0xe9, 0xda, 0xd9, 0x8e, 0x0e, 0xde, 0x60, 0xa2, 0x77, 0xb8, 0xcc, 0x78, 0x84, + 0xbf, 0xfc, 0x88, 0xc7, 0x3c, 0xc1, 0x4b, 0xb1, 0xf2, 0x02, 0x19, 0xe6, 0x25, 0x7f, 0x87, 0x39, 0xd5, 0xea, 0x90, + 0x60, 0x86, 0x01, 0x83, 0x57, 0x6c, 0x1c, 0x47, 0x8e, 0xed, 0xcc, 0x61, 0xc7, 0xc2, 0x98, 0xad, 0x5a, 0x42, 0x33, + 0xe5, 0x32, 0xbb, 0xb6, 0xfa, 0x7d, 0x3b, 0x39, 0x7e, 0xbf, 0x2c, 0x3c, 0x94, 0x01, 0x46, 0x5b, 0x7a, 0x00, 0x30, + 0xbe, 0x2a, 0xc9, 0x51, 0xd8, 0x57, 0x56, 0x83, 0x2d, 0xcc, 0x86, 0x8e, 0xdf, 0x05, 0x37, 0x82, 0x8a, 0xf1, 0x7b, + 0x50, 0x3f, 0x38, 0xad, 0x6d, 0x30, 0x6b, 0x8c, 0x6e, 0x7a, 0xa0, 0xe1, 0x4a, 0x18, 0x49, 0x04, 0x07, 0x1a, 0xa5, + 0x9e, 0xfe, 0x05, 0x64, 0x55, 0xb8, 0xa8, 0x78, 0x7c, 0x71, 0x20, 0xef, 0x7c, 0xdb, 0x18, 0xb9, 0xa5, 0x88, 0x7d, + 0xf5, 0xbd, 0xa9, 0x4d, 0x50, 0x17, 0xf4, 0x5b, 0x20, 0xe9, 0xdc, 0x1b, 0x35, 0x02, 0xa6, 0x5c, 0x5b, 0xd2, 0x73, + 0x08, 0x6d, 0xa1, 0x0f, 0xc6, 0xec, 0x34, 0x1e, 0x49, 0xb1, 0xee, 0x59, 0xf2, 0xaa, 0x48, 0x8b, 0xb0, 0x08, 0x3b, + 0x9e, 0xf0, 0x9d, 0xe1, 0x05, 0xb5, 0x5a, 0x98, 0x66, 0x76, 0xff, 0x5e, 0x4f, 0x43, 0x52, 0xcf, 0x56, 0xb7, 0xf1, + 0x57, 0x52, 0x1e, 0x82, 0xaf, 0xf6, 0xf7, 0xe1, 0x3d, 0xfc, 0xa5, 0x94, 0xf7, 0x86, 0xb6, 0xeb, 0x93, 0x50, 0xbc, + 0x57, 0xfd, 0x66, 0x4a, 0x94, 0x08, 0x9b, 0xa0, 0xbf, 0xbc, 0xdb, 0x2a, 0x32, 0xa9, 0xb4, 0xba, 0x3b, 0x95, 0xd2, + 0x82, 0x67, 0x43, 0x4a, 0x81, 0x00, 0xed, 0xfa, 0x3b, 0x86, 0x28, 0x3c, 0x6d, 0xe1, 0xcf, 0x9a, 0x30, 0xbc, 0x0f, + 0x0d, 0x94, 0x34, 0x7c, 0x09, 0xcd, 0xb7, 0x85, 0xe0, 0x85, 0x7e, 0x3f, 0x92, 0xa8, 0x12, 0x62, 0xaa, 0xce, 0x31, + 0x6b, 0x0e, 0x91, 0x44, 0x8e, 0x80, 0xed, 0x19, 0xf1, 0x26, 0xc1, 0xae, 0x32, 0x9a, 0xf2, 0x14, 0xfa, 0x3a, 0xfa, + 0x33, 0xce, 0xeb, 0xea, 0xbc, 0xda, 0xce, 0x59, 0x33, 0x05, 0x32, 0x7c, 0xe3, 0xa0, 0x8a, 0xae, 0x2e, 0x88, 0xcf, + 0x99, 0x89, 0x6d, 0x5c, 0x7d, 0xf0, 0x6d, 0x4d, 0xf6, 0xad, 0xb9, 0x29, 0x58, 0xc5, 0x34, 0xb4, 0x2f, 0x30, 0x65, + 0x06, 0x7f, 0x56, 0xc5, 0xea, 0x41, 0x32, 0x94, 0x9f, 0x44, 0xf8, 0xdb, 0x58, 0xe8, 0x47, 0x59, 0x6d, 0x40, 0x4e, + 0xdf, 0xab, 0x24, 0x48, 0x5f, 0x8c, 0xcb, 0x26, 0x12, 0x60, 0x2f, 0xe0, 0x2f, 0xf7, 0xab, 0xae, 0x4a, 0xc8, 0x3b, + 0x90, 0x98, 0x53, 0x30, 0x8e, 0x73, 0xba, 0x5a, 0xab, 0xf0, 0xaf, 0x45, 0x34, 0x2b, 0x52, 0xd3, 0xae, 0x64, 0xc5, + 0xc0, 0xc6, 0x22, 0x3b, 0x90, 0xc9, 0x68, 0xe6, 0x07, 0x9b, 0xcd, 0xbb, 0x8f, 0x63, 0x91, 0x87, 0x86, 0x1f, 0xb4, + 0xb7, 0x05, 0x91, 0x6d, 0x10, 0x63, 0x57, 0xe2, 0x44, 0xc6, 0x0d, 0x5e, 0x19, 0xac, 0x7e, 0x43, 0x91, 0xb9, 0xe1, + 0x6d, 0x73, 0xb5, 0xf4, 0xb8, 0xb4, 0x0e, 0xae, 0x8c, 0xdf, 0x1d, 0xb3, 0x88, 0xfb, 0x51, 0x4a, 0xb9, 0x49, 0x8e, + 0x21, 0x16, 0xbc, 0x0e, 0xdb, 0x76, 0x4b, 0x90, 0x3c, 0xc6, 0xaf, 0x70, 0x12, 0xa4, 0xf7, 0xa1, 0xb0, 0x4a, 0xd8, + 0xda, 0x9d, 0x76, 0xfb, 0x6f, 0x0e, 0xf6, 0x2c, 0xb1, 0x9b, 0x77, 0xb7, 0xe0, 0x75, 0x97, 0xdc, 0x61, 0x91, 0x9f, + 0x11, 0x8a, 0xfc, 0x0c, 0x4b, 0x24, 0x74, 0x85, 0xf6, 0x96, 0x40, 0xd3, 0xb6, 0x58, 0x3a, 0x12, 0x31, 0xbc, 0x19, + 0xb8, 0x0b, 0x31, 0x7e, 0xd4, 0x6b, 0x0b, 0xbb, 0xb5, 0x70, 0xa5, 0x6d, 0x95, 0xe1, 0xa2, 0x0c, 0x04, 0x9e, 0xaa, + 0x88, 0x1f, 0xa8, 0x75, 0xa6, 0x92, 0x5d, 0xe4, 0x50, 0x3a, 0x27, 0x75, 0xb5, 0x75, 0xb1, 0x38, 0x9e, 0x81, 0x1c, + 0x52, 0x09, 0x2a, 0xef, 0x65, 0x87, 0x5d, 0x9a, 0x0a, 0x93, 0x62, 0x57, 0x23, 0x92, 0xd3, 0x4e, 0x7f, 0x37, 0x92, + 0xf6, 0x0e, 0xee, 0xdd, 0x02, 0x36, 0x2f, 0xa8, 0x39, 0x34, 0x2a, 0xfc, 0x38, 0xdb, 0x3a, 0x63, 0xc7, 0xad, 0x68, + 0x1e, 0x57, 0xe1, 0x3f, 0xd4, 0x7e, 0xfd, 0x5d, 0xa5, 0x08, 0x65, 0x9a, 0xa5, 0x7c, 0x8c, 0x8c, 0x2c, 0x0e, 0x24, + 0x1c, 0x31, 0x68, 0x29, 0x63, 0x8b, 0x64, 0x34, 0x02, 0xf1, 0x01, 0x56, 0xe2, 0x5f, 0x15, 0x83, 0x94, 0x9a, 0xa0, + 0xb4, 0xfb, 0x7f, 0xfd, 0x5f, 0xff, 0x5b, 0x86, 0x15, 0x81, 0xac, 0x00, 0x16, 0xa6, 0xc1, 0x54, 0x27, 0x8c, 0xec, + 0x1c, 0x1c, 0xd1, 0x78, 0xdc, 0x9a, 0x46, 0xc9, 0x04, 0x20, 0x28, 0x98, 0xb8, 0xca, 0x24, 0xeb, 0x81, 0x0b, 0x24, + 0x58, 0xe6, 0xe1, 0xbc, 0x04, 0xaf, 0x5e, 0x84, 0x2b, 0xf6, 0xbb, 0xf2, 0x56, 0x55, 0xbe, 0x30, 0x31, 0xb4, 0x91, + 0xc5, 0x6a, 0xf0, 0x5c, 0x2d, 0x93, 0x55, 0xfd, 0x82, 0x24, 0x29, 0x3c, 0x58, 0x2d, 0x8d, 0x15, 0x5a, 0xea, 0x83, + 0x90, 0x7f, 0xfb, 0xe7, 0xff, 0xfc, 0xdf, 0xd5, 0x2b, 0x9e, 0x6f, 0xfc, 0xf5, 0x9f, 0xfe, 0xe1, 0xff, 0xfe, 0x9f, + 0xff, 0x82, 0x59, 0xc2, 0xf2, 0x0c, 0x84, 0xb6, 0x92, 0x55, 0x1d, 0x80, 0x88, 0x3d, 0x65, 0x55, 0x0e, 0x47, 0x3d, + 0xdd, 0x75, 0x9f, 0x26, 0x24, 0xde, 0x94, 0xd0, 0x11, 0x5f, 0x53, 0x7a, 0x34, 0x51, 0xed, 0x1a, 0xf2, 0xc1, 0x52, + 0x5a, 0x74, 0xac, 0x6f, 0xef, 0xb4, 0xed, 0x6a, 0x79, 0xfb, 0x46, 0xdf, 0x2d, 0x5c, 0x98, 0x5b, 0x65, 0xe0, 0xf8, + 0x7a, 0xd9, 0x96, 0x2a, 0x8c, 0x85, 0x25, 0x65, 0x55, 0x6e, 0x61, 0x7c, 0x79, 0x89, 0xaf, 0x41, 0xd7, 0x28, 0xa6, + 0x55, 0xae, 0xf5, 0xe9, 0xfd, 0xb2, 0x00, 0x44, 0x27, 0xb8, 0x34, 0x22, 0x58, 0x46, 0x67, 0xa7, 0x2d, 0xb4, 0x4e, + 0x92, 0x8b, 0x92, 0x46, 0x11, 0xde, 0xcc, 0xfd, 0x47, 0x7f, 0x57, 0xfe, 0x69, 0x86, 0x56, 0x81, 0xe5, 0xcc, 0xa2, + 0x73, 0xe9, 0xe3, 0x3c, 0x68, 0xb7, 0xe7, 0xe7, 0xee, 0xb2, 0x9a, 0xc1, 0xbb, 0x6a, 0x32, 0x0a, 0xb0, 0x99, 0x03, + 0xd2, 0xa1, 0xab, 0x8e, 0xe5, 0x81, 0x59, 0xdf, 0xc6, 0xd0, 0x4f, 0x59, 0x7e, 0xb9, 0xa4, 0x70, 0x52, 0xfc, 0x1b, + 0x1e, 0x8e, 0xca, 0xc8, 0x1b, 0x94, 0x18, 0x58, 0x2c, 0x8d, 0x5e, 0x5d, 0xd1, 0x6b, 0xda, 0x59, 0xcd, 0x4d, 0x31, + 0x0f, 0x77, 0xcd, 0x63, 0xd9, 0xfb, 0x78, 0xd0, 0x3a, 0xed, 0x78, 0xd3, 0xee, 0x52, 0x0f, 0xcf, 0x79, 0x36, 0x33, + 0x4f, 0x73, 0x59, 0xc4, 0x46, 0x6c, 0xa2, 0x22, 0x96, 0xb2, 0x5e, 0x9c, 0xd4, 0x96, 0x5f, 0xe0, 0x76, 0x03, 0xda, + 0x66, 0x11, 0x0f, 0x88, 0x69, 0x7b, 0xe6, 0x79, 0x6f, 0x84, 0x27, 0xe9, 0xd9, 0xd2, 0x98, 0xab, 0x27, 0x9a, 0x62, + 0x5c, 0xb0, 0x9e, 0xf7, 0x53, 0xfa, 0xd4, 0xdd, 0x1c, 0x4a, 0x84, 0x15, 0x5e, 0xc8, 0x63, 0xd4, 0x77, 0x35, 0x7f, + 0x5c, 0x8a, 0x62, 0x70, 0x81, 0xd7, 0xd6, 0x0b, 0xb5, 0x28, 0x6a, 0x5f, 0x80, 0xb5, 0x43, 0x60, 0xda, 0xcd, 0x56, + 0x54, 0x88, 0xad, 0xde, 0x85, 0x2f, 0xb4, 0xed, 0x1d, 0xcd, 0xe7, 0xd4, 0xd0, 0x05, 0x6e, 0x24, 0x1b, 0x1a, 0x25, + 0x05, 0xa5, 0x08, 0x88, 0x13, 0x79, 0xd9, 0x46, 0xb2, 0xad, 0x78, 0x92, 0x67, 0xf5, 0xf4, 0xfb, 0xb6, 0xff, 0x1f, + 0x22, 0x28, 0x4d, 0x5d, 0x85, 0x7b, 0x00, 0x00}; } // namespace web_server } // namespace esphome diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 5c06a97d47..30ac959e43 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -360,9 +360,14 @@ void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlM } std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail start_config) { return json::build_json([obj, value, start_config](JsonObject root) { - std::string state = value_accuracy_to_string(value, obj->get_accuracy_decimals()); - if (!obj->get_unit_of_measurement().empty()) - state += " " + obj->get_unit_of_measurement(); + std::string state; + if (isnan(value)) { + state = "NA"; + } else { + state = value_accuracy_to_string(value, obj->get_accuracy_decimals()); + if (!obj->get_unit_of_measurement().empty()) + state += " " + obj->get_unit_of_measurement(); + } set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config); }); } @@ -719,12 +724,15 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail root["step"] = obj->traits.get_step(); root["mode"] = (int) obj->traits.get_mode(); } - std::string state = str_sprintf("%f", value); - root["state"] = state; if (isnan(value)) { root["value"] = "\"NaN\""; + root["state"] = "NA"; } else { root["value"] = value; + std::string state = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step())); + if (!obj->traits.get_unit_of_measurement().empty()) + state += " " + obj->traits.get_unit_of_measurement(); + root["state"] = state; } }); } diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index b03d890ad8..d82e452c3d 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -258,6 +258,19 @@ std::string value_accuracy_to_string(float value, int8_t accuracy_decimals) { return std::string(tmp); } +int8_t step_to_accuracy_decimals(float step) { + // use printf %g to find number of digits based on temperature step + char buf[32]; + sprintf(buf, "%.5g", step); + + std::string str{buf}; + size_t dot_pos = str.find('.'); + if (dot_pos == std::string::npos) + return 0; + + return str.length() - dot_pos - 1; +} + // Colors float gamma_correct(float value, float gamma) { diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index dab829ab93..6bed743010 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -415,6 +415,9 @@ ParseOnOffState parse_on_off(const char *str, const char *on = nullptr, const ch /// Create a string from a value and an accuracy in decimals. std::string value_accuracy_to_string(float value, int8_t accuracy_decimals); +/// Derive accuracy in decimals from an increment step. +int8_t step_to_accuracy_decimals(float step); + ///@} /// @name Colors From 0688deca6b07d6bc5ba1cca6c92410be18f43172 Mon Sep 17 00:00:00 2001 From: puuu Date: Mon, 15 Aug 2022 07:56:25 +0900 Subject: [PATCH 08/66] Add support for pvvx mithermometer display via ble client (#3333) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../pvvx_mithermometer/display/__init__.py | 56 +++++++ .../display/pvvx_display.cpp | 154 ++++++++++++++++++ .../pvvx_mithermometer/display/pvvx_display.h | 133 +++++++++++++++ esphome/const.py | 2 + tests/test1.yaml | 15 ++ 5 files changed, 360 insertions(+) create mode 100644 esphome/components/pvvx_mithermometer/display/__init__.py create mode 100644 esphome/components/pvvx_mithermometer/display/pvvx_display.cpp create mode 100644 esphome/components/pvvx_mithermometer/display/pvvx_display.h diff --git a/esphome/components/pvvx_mithermometer/display/__init__.py b/esphome/components/pvvx_mithermometer/display/__init__.py new file mode 100644 index 0000000000..d935638933 --- /dev/null +++ b/esphome/components/pvvx_mithermometer/display/__init__.py @@ -0,0 +1,56 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import ble_client, display, time +from esphome.const import ( + CONF_AUTO_CLEAR_ENABLED, + CONF_DISCONNECT_DELAY, + CONF_ID, + CONF_LAMBDA, + CONF_TIME_ID, + CONF_VALIDITY_PERIOD, +) + +DEPENDENCIES = ["ble_client"] + +pvvx_ns = cg.esphome_ns.namespace("pvvx_mithermometer") +PVVXDisplay = pvvx_ns.class_( + "PVVXDisplay", cg.PollingComponent, ble_client.BLEClientNode +) +PVVXDisplayRef = PVVXDisplay.operator("ref") + +CONFIG_SCHEMA = ( + display.BASIC_DISPLAY_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(PVVXDisplay), + cv.Optional(CONF_TIME_ID): cv.use_id(time.RealTimeClock), + cv.Optional(CONF_AUTO_CLEAR_ENABLED, default=True): cv.boolean, + cv.Optional(CONF_DISCONNECT_DELAY, default="5s"): cv.positive_time_period, + cv.Optional(CONF_VALIDITY_PERIOD, default="5min"): cv.All( + cv.positive_time_period_seconds, + cv.Range(max=cv.TimePeriod(seconds=65535)), + ), + } + ) + .extend(ble_client.BLE_CLIENT_SCHEMA) + .extend(cv.polling_component_schema("60s")) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await display.register_display(var, config) + await ble_client.register_ble_node(var, config) + cg.add(var.set_disconnect_delay(config[CONF_DISCONNECT_DELAY].total_milliseconds)) + cg.add(var.set_auto_clear(config[CONF_AUTO_CLEAR_ENABLED])) + cg.add(var.set_validity_period(config[CONF_VALIDITY_PERIOD].total_seconds)) + + if CONF_TIME_ID in config: + time_ = await cg.get_variable(config[CONF_TIME_ID]) + cg.add(var.set_time(time_)) + + if CONF_LAMBDA in config: + lambda_ = await cg.process_lambda( + config[CONF_LAMBDA], [(PVVXDisplayRef, "it")], return_type=cg.void + ) + cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp b/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp new file mode 100644 index 0000000000..21638ef7e4 --- /dev/null +++ b/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp @@ -0,0 +1,154 @@ +#include "pvvx_display.h" +#include "esphome/core/log.h" + +#ifdef USE_ESP32 +namespace esphome { +namespace pvvx_mithermometer { + +static const char *const TAG = "display.pvvx_mithermometer"; + +void PVVXDisplay::dump_config() { + ESP_LOGCONFIG(TAG, "PVVX MiThermometer display:"); + ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent_->address_str().c_str()); + ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str()); + ESP_LOGCONFIG(TAG, " Characteristic UUID : %s", this->char_uuid_.to_string().c_str()); + ESP_LOGCONFIG(TAG, " Auto clear : %s", YESNO(this->auto_clear_enabled_)); + ESP_LOGCONFIG(TAG, " Set time on connection: %s", YESNO(this->time_ != nullptr)); + ESP_LOGCONFIG(TAG, " Disconnect delay : %dms", this->disconnect_delay_ms_); + LOG_UPDATE_INTERVAL(this); +} + +void PVVXDisplay::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) { + switch (event) { + case ESP_GATTC_OPEN_EVT: + ESP_LOGV(TAG, "[%s] Connected successfully!", this->parent_->address_str().c_str()); + this->delayed_disconnect_(); + break; + case ESP_GATTC_DISCONNECT_EVT: + ESP_LOGV(TAG, "[%s] Disconnected", this->parent_->address_str().c_str()); + this->connection_established_ = false; + this->cancel_timeout("disconnect"); + this->char_handle_ = 0; + break; + case ESP_GATTC_SEARCH_CMPL_EVT: { + auto *chr = this->parent_->get_characteristic(this->service_uuid_, this->char_uuid_); + if (chr == nullptr) { + ESP_LOGW(TAG, "[%s] Characteristic not found.", this->parent_->address_str().c_str()); + break; + } + this->connection_established_ = true; + this->char_handle_ = chr->handle; +#ifdef USE_TIME + this->sync_time_(); +#endif + this->display(); + break; + } + default: + break; + } +} + +void PVVXDisplay::update() { + if (this->auto_clear_enabled_) + this->clear(); + if (this->writer_.has_value()) + (*this->writer_)(*this); + this->display(); +} + +void PVVXDisplay::display() { + if (!this->parent_->enabled) { + ESP_LOGD(TAG, "[%s] BLE client not enabled. Init connection.", this->parent_->address_str().c_str()); + this->parent_->set_enabled(true); + return; + } + if (!this->connection_established_) { + ESP_LOGW(TAG, "[%s] Not connected to BLE client. State update can not be written.", + this->parent_->address_str().c_str()); + return; + } + if (!this->char_handle_) { + ESP_LOGW(TAG, "[%s] No ble handle to BLE client. State update can not be written.", + this->parent_->address_str().c_str()); + return; + } + ESP_LOGD(TAG, "[%s] Send to display: bignum %d, smallnum: %d, cfg: 0x%02x, validity period: %u.", + this->parent_->address_str().c_str(), this->bignum_, this->smallnum_, this->cfg_, this->validity_period_); + uint8_t blk[8] = {}; + blk[0] = 0x22; + blk[1] = this->bignum_ & 0xff; + blk[2] = (this->bignum_ >> 8) & 0xff; + blk[3] = this->smallnum_ & 0xff; + blk[4] = (this->smallnum_ >> 8) & 0xff; + blk[5] = this->validity_period_ & 0xff; + blk[6] = (this->validity_period_ >> 8) & 0xff; + blk[7] = this->cfg_; + this->send_to_setup_char_(blk, sizeof(blk)); +} + +void PVVXDisplay::setcfgbit_(uint8_t bit, bool value) { + uint8_t mask = 1 << bit; + if (value) { + this->cfg_ |= mask; + } else { + this->cfg_ &= (0xFF ^ mask); + } +} + +void PVVXDisplay::send_to_setup_char_(uint8_t *blk, size_t size) { + if (!this->connection_established_) { + ESP_LOGW(TAG, "[%s] Not connected to BLE client.", this->parent_->address_str().c_str()); + return; + } + auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, size, blk, + ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + if (status) { + ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); + } else { + ESP_LOGV(TAG, "[%s] send %u bytes", this->parent_->address_str().c_str(), size); + this->delayed_disconnect_(); + } +} + +void PVVXDisplay::delayed_disconnect_() { + if (this->disconnect_delay_ms_ == 0) + return; + this->cancel_timeout("disconnect"); + this->set_timeout("disconnect", this->disconnect_delay_ms_, [this]() { this->parent_->set_enabled(false); }); +} + +#ifdef USE_TIME +void PVVXDisplay::sync_time_() { + if (this->time_ == nullptr) + return; + if (!this->connection_established_) { + ESP_LOGW(TAG, "[%s] Not connected to BLE client. Time can not be synced.", this->parent_->address_str().c_str()); + return; + } + if (!this->char_handle_) { + ESP_LOGW(TAG, "[%s] No ble handle to BLE client. Time can not be synced.", this->parent_->address_str().c_str()); + return; + } + auto time = this->time_->now(); + if (!time.is_valid()) { + ESP_LOGW(TAG, "[%s] Time is not yet valid. Time can not be synced.", this->parent_->address_str().c_str()); + return; + } + time.recalc_timestamp_utc(true); // calculate timestamp of local time + uint8_t blk[5] = {}; + ESP_LOGD(TAG, "[%s] Sync time with timestamp %lu.", this->parent_->address_str().c_str(), time.timestamp); + blk[0] = 0x23; + blk[1] = time.timestamp & 0xff; + blk[2] = (time.timestamp >> 8) & 0xff; + blk[3] = (time.timestamp >> 16) & 0xff; + blk[4] = (time.timestamp >> 24) & 0xff; + this->send_to_setup_char_(blk, sizeof(blk)); +} +#endif + +} // namespace pvvx_mithermometer +} // namespace esphome + +#endif diff --git a/esphome/components/pvvx_mithermometer/display/pvvx_display.h b/esphome/components/pvvx_mithermometer/display/pvvx_display.h new file mode 100644 index 0000000000..4de1ab7ba6 --- /dev/null +++ b/esphome/components/pvvx_mithermometer/display/pvvx_display.h @@ -0,0 +1,133 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/defines.h" +#include "esphome/components/ble_client/ble_client.h" + +#ifdef USE_ESP32 +#include +#ifdef USE_TIME +#include "esphome/components/time/real_time_clock.h" +#endif + +namespace esphome { +namespace pvvx_mithermometer { + +class PVVXDisplay; + +/// Possible units for the big number +enum UNIT { + UNIT_NONE = 0, ///< do not show a unit + UNIT_DEG_GHE, ///< show "°Г" + UNIT_MINUS, ///< show " -" + UNIT_DEG_F, ///< show "°F" + UNIT_LOWDASH, ///< show " _" + UNIT_DEG_C, ///< show "°C" + UNIT_LINES, ///< show " =" + UNIT_DEG_E, ///< show "°E" +}; + +using pvvx_writer_t = std::function; + +class PVVXDisplay : public ble_client::BLEClientNode, public PollingComponent { + public: + void set_writer(pvvx_writer_t &&writer) { this->writer_ = writer; } + void set_auto_clear(bool auto_clear_enabled) { this->auto_clear_enabled_ = auto_clear_enabled; } + void set_disconnect_delay(uint32_t ms) { this->disconnect_delay_ms_ = ms; } + + void dump_config() override; + + float get_setup_priority() const override { return setup_priority::DATA; } + + void update() override; + + void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) override; + + /// Set validity period of the display information in seconds (1..65535) + void set_validity_period(uint16_t validity_period) { this->validity_period_ = validity_period; } + /// Clear the screen + void clear() { + this->bignum_ = 0; + this->smallnum_ = 0; + this->cfg_ = 0; + } + /** + * Print the big number + * + * Valid values are from -99.5 to 1999.5. Smaller values are displayed as Lo, higher as Hi. + * It will printed as it fits in the screen. + */ + void print_bignum(float bignum) { this->bignum_ = bignum * 10; } + /** + * Print the small number + * + * Valid values are from -9 to 99. Smaller values are displayed as Lo, higher as Hi. + */ + void print_smallnum(float smallnum) { this->smallnum_ = smallnum; } + /** + * Print a happy face + * + * Can be combined with print_sad() print_bracket(). + * Possible ouputs are: + * + * @verbatim + * bracket sad happy + * 0 0 0 " " + * 0 0 1 " ^_^ " + * 0 1 0 " -∧- " + * 0 1 1 " Δ△Δ " + * 1 0 0 "( )" + * 1 0 1 "(^_^)" + * 1 1 0 "(-∧-)" + * 1 1 1 "(Δ△Δ)" + * @endverbatim + */ + void print_happy(bool happy = true) { this->setcfgbit_(0, happy); } + /// Print a sad face + void print_sad(bool sad = true) { this->setcfgbit_(1, sad); } + /// Print round brackets around the face + void print_bracket(bool bracket = true) { this->setcfgbit_(2, bracket); } + /// Print percent sign at small number + void print_percent(bool percent = true) { this->setcfgbit_(3, percent); } + /// Print battery sign + void print_battery(bool battery = true) { this->setcfgbit_(4, battery); } + /// Print unit of big number + void print_unit(UNIT unit) { this->cfg_ = (this->cfg_ & 0x1F) | ((unit & 0x7) << 5); } + + void display(); + +#ifdef USE_TIME + void set_time(time::RealTimeClock *time) { this->time_ = time; }; +#endif + + protected: + bool auto_clear_enabled_{true}; + uint32_t disconnect_delay_ms_ = 5000; + uint16_t validity_period_ = 300; + uint16_t bignum_ = 0; + uint16_t smallnum_ = 0; + uint8_t cfg_ = 0; + + void setcfgbit_(uint8_t bit, bool value); + void send_to_setup_char_(uint8_t *blk, size_t size); + void delayed_disconnect_(); +#ifdef USE_TIME + void sync_time_(); + time::RealTimeClock *time_ = nullptr; +#endif + uint16_t char_handle_ = 0; + bool connection_established_ = false; + + esp32_ble_tracker::ESPBTUUID service_uuid_ = + esp32_ble_tracker::ESPBTUUID::from_raw("00001f10-0000-1000-8000-00805f9b34fb"); + esp32_ble_tracker::ESPBTUUID char_uuid_ = + esp32_ble_tracker::ESPBTUUID::from_raw("00001f1f-0000-1000-8000-00805f9b34fb"); + + optional writer_{}; +}; + +} // namespace pvvx_mithermometer +} // namespace esphome + +#endif diff --git a/esphome/const.py b/esphome/const.py index 6f5eaabfcb..b3e038fe20 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -173,6 +173,7 @@ CONF_DIR_PIN = "dir_pin" CONF_DIRECTION = "direction" CONF_DIRECTION_OUTPUT = "direction_output" CONF_DISABLED_BY_DEFAULT = "disabled_by_default" +CONF_DISCONNECT_DELAY = "disconnect_delay" CONF_DISCOVERY = "discovery" CONF_DISCOVERY_OBJECT_ID_GENERATOR = "discovery_object_id_generator" CONF_DISCOVERY_PREFIX = "discovery_prefix" @@ -736,6 +737,7 @@ CONF_USE_ABBREVIATIONS = "use_abbreviations" CONF_USE_ADDRESS = "use_address" CONF_USERNAME = "username" CONF_UUID = "uuid" +CONF_VALIDITY_PERIOD = "validity_period" CONF_VALUE = "value" CONF_VALUE_FONT = "value_font" CONF_VARIABLES = "variables" diff --git a/tests/test1.yaml b/tests/test1.yaml index 5897639ead..387aab6bbe 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2436,6 +2436,21 @@ display: it.fill(Color::WHITE); id(glob_bool_processed) = true; } + - platform: pvvx_mithermometer + ble_client_id: ble_foo + time_id: sntp_time + disconnect_delay: 3s + update_interval: 10min + validity_period: 20min + lambda: |- + it.print_bignum(188.8); + it.print_unit(pvvx_mithermometer::UNIT_DEG_E); + it.print_smallnum(88); + it.print_percent(true); + it.print_happy(true); + it.print_sad(true); + it.print_bracket(true); + it.print_battery(true); tm1651: id: tm1651_battery From 8df27d4c3fa027a19bb18ca28a862646da4f0767 Mon Sep 17 00:00:00 2001 From: buxtronix Date: Mon, 15 Aug 2022 13:06:05 +1000 Subject: [PATCH 09/66] Only trigger ble_client on_connect after discovering services (#3710) --- esphome/components/ble_client/automation.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/ble_client/automation.h b/esphome/components/ble_client/automation.h index 12c22345b4..828cbca662 100644 --- a/esphome/components/ble_client/automation.h +++ b/esphome/components/ble_client/automation.h @@ -15,10 +15,10 @@ class BLEClientConnectTrigger : public Trigger<>, public BLEClientNode { void loop() override {} void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override { - if (event == ESP_GATTC_OPEN_EVT && param->open.status == ESP_GATT_OK) - this->trigger(); - if (event == ESP_GATTC_SEARCH_CMPL_EVT) + if (event == ESP_GATTC_SEARCH_CMPL_EVT) { this->node_state = espbt::ClientState::ESTABLISHED; + this->trigger(); + } } }; From 1a2288cccf3fbb3448d885c704ce3a343fe388ba Mon Sep 17 00:00:00 2001 From: rbaron Date: Mon, 15 Aug 2022 05:09:33 +0200 Subject: [PATCH 10/66] Fixes BLE remote address type when connecting (#3702) --- esphome/components/ble_client/ble_client.cpp | 3 ++- esphome/components/ble_client/ble_client.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index d06c3c4cad..5f58d8273f 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -54,6 +54,7 @@ bool BLEClient::parse_device(const espbt::ESPBTDevice &device) { this->remote_bda[3] = (addr >> 16) & 0xFF; this->remote_bda[4] = (addr >> 8) & 0xFF; this->remote_bda[5] = (addr >> 0) & 0xFF; + this->remote_addr_type = device.get_address_type(); return true; } @@ -83,7 +84,7 @@ void BLEClient::set_enabled(bool enabled) { void BLEClient::connect() { ESP_LOGI(TAG, "Attempting BLE connection to %s", this->address_str().c_str()); - auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, BLE_ADDR_TYPE_PUBLIC, true); + auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, this->remote_addr_type, true); if (ret) { ESP_LOGW(TAG, "esp_ble_gattc_open error, address=%s status=%d", this->address_str().c_str(), ret); this->set_states_(espbt::ClientState::IDLE); diff --git a/esphome/components/ble_client/ble_client.h b/esphome/components/ble_client/ble_client.h index b122bfd11e..5ed8f219d1 100644 --- a/esphome/components/ble_client/ble_client.h +++ b/esphome/components/ble_client/ble_client.h @@ -115,6 +115,7 @@ class BLEClient : public espbt::ESPBTClient, public Component { int gattc_if; esp_bd_addr_t remote_bda; + esp_ble_addr_type_t remote_addr_type; uint16_t conn_id; uint64_t address; bool enabled; From 1a524a5a50a3e5630d10c43a8240cd6b74c11d86 Mon Sep 17 00:00:00 2001 From: rbaron Date: Tue, 16 Aug 2022 04:40:58 +0200 Subject: [PATCH 11/66] Makes ble_client.ble_write's action value templatable (#3715) --- esphome/components/ble_client/__init__.py | 10 +++++-- esphome/components/ble_client/automation.cpp | 9 +++--- esphome/components/ble_client/automation.h | 30 ++++++++++++++++---- tests/test2.yaml | 6 ++++ 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/esphome/components/ble_client/__init__.py b/esphome/components/ble_client/__init__.py index 6c13e7fcf5..4b7d5f5b8a 100644 --- a/esphome/components/ble_client/__init__.py +++ b/esphome/components/ble_client/__init__.py @@ -82,7 +82,7 @@ BLE_WRITE_ACTION_SCHEMA = cv.Schema( cv.Required(CONF_ID): cv.use_id(BLEClient), cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid, - cv.Required(CONF_VALUE): cv.ensure_list(cv.hex_uint8_t), + cv.Required(CONF_VALUE): cv.templatable(cv.ensure_list(cv.hex_uint8_t)), } ) @@ -93,8 +93,14 @@ BLE_WRITE_ACTION_SCHEMA = cv.Schema( async def ble_write_to_code(config, action_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) + value = config[CONF_VALUE] - cg.add(var.set_value(value)) + if cg.is_template(value): + templ = await cg.templatable(value, args, cg.std_vector.template(cg.uint8)) + cg.add(var.set_value_template(templ)) + else: + cg.add(var.set_value_simple(value)) + serv_uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID]) cg.add(var.set_service_uuid128(serv_uuid128)) char_uuid128 = esp32_ble_tracker.as_reversed_hex_array( diff --git a/esphome/components/ble_client/automation.cpp b/esphome/components/ble_client/automation.cpp index 8d5fe96570..6918ab31b4 100644 --- a/esphome/components/ble_client/automation.cpp +++ b/esphome/components/ble_client/automation.cpp @@ -10,7 +10,7 @@ namespace esphome { namespace ble_client { static const char *const TAG = "ble_client.automation"; -void BLEWriterClientNode::write() { +void BLEWriterClientNode::write(const std::vector &value) { if (this->node_state != espbt::ClientState::ESTABLISHED) { ESP_LOGW(TAG, "Cannot write to BLE characteristic - not connected"); return; @@ -29,9 +29,10 @@ void BLEWriterClientNode::write() { ESP_LOGE(TAG, "Characteristic %s does not allow writing", this->char_uuid_.to_string().c_str()); return; } - ESP_LOGVV(TAG, "Will write %d bytes: %s", this->value_.size(), format_hex_pretty(this->value_).c_str()); - esp_err_t err = esp_ble_gattc_write_char(this->parent()->gattc_if, this->parent()->conn_id, this->ble_char_handle_, - value_.size(), value_.data(), write_type, ESP_GATT_AUTH_REQ_NONE); + ESP_LOGVV(TAG, "Will write %d bytes: %s", value.size(), format_hex_pretty(value).c_str()); + esp_err_t err = + esp_ble_gattc_write_char(this->parent()->gattc_if, this->parent()->conn_id, this->ble_char_handle_, value.size(), + const_cast(value.data()), write_type, ESP_GATT_AUTH_REQ_NONE); if (err != ESP_OK) { ESP_LOGE(TAG, "Error writing to characteristic: %s!", esp_err_to_name(err)); } diff --git a/esphome/components/ble_client/automation.h b/esphome/components/ble_client/automation.h index 828cbca662..38e64ebd76 100644 --- a/esphome/components/ble_client/automation.h +++ b/esphome/components/ble_client/automation.h @@ -42,10 +42,8 @@ class BLEWriterClientNode : public BLEClientNode { ble_client_ = ble_client; } - void set_value(std::vector value) { value_ = std::move(value); } - - // Attempts to write the contents of value_ to char_uuid_. - void write(); + // Attempts to write the contents of value to char_uuid_. + void write(const std::vector &value); void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } @@ -60,14 +58,34 @@ class BLEWriterClientNode : public BLEClientNode { esp_gatt_char_prop_t char_props_; espbt::ESPBTUUID service_uuid_; espbt::ESPBTUUID char_uuid_; - std::vector value_; }; template class BLEClientWriteAction : public Action, public BLEWriterClientNode { public: BLEClientWriteAction(BLEClient *ble_client) : BLEWriterClientNode(ble_client) {} - void play(Ts... x) override { return write(); } + void play(Ts... x) override { + if (has_simple_value_) { + return write(this->value_simple_); + } else { + return write(this->value_template_(x...)); + } + } + + void set_value_template(std::function(Ts...)> func) { + this->value_template_ = std::move(func); + has_simple_value_ = false; + } + + void set_value_simple(const std::vector &value) { + this->value_simple_ = value; + has_simple_value_ = true; + } + + private: + bool has_simple_value_ = true; + std::vector value_simple_; + std::function(Ts...)> value_template_{}; }; } // namespace ble_client diff --git a/tests/test2.yaml b/tests/test2.yaml index 5dd9e76a2f..110e8e6625 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -615,3 +615,9 @@ switch: service_uuid: F61E3BE9-2826-A81B-970A-4D4DECFABBAE characteristic_uuid: 6490FAFE-0734-732C-8705-91B653A081FC value: [0x01, 0xab, 0xff] + - ble_client.ble_write: + id: airthings01 + service_uuid: F61E3BE9-2826-A81B-970A-4D4DECFABBAE + characteristic_uuid: 6490FAFE-0734-732C-8705-91B653A081FC + value: !lambda |- + return {0x13, 0x37}; From df6830110d20f49ad21811085ce9e1005fb3b785 Mon Sep 17 00:00:00 2001 From: Ignacio Hernandez-Ros Date: Tue, 16 Aug 2022 11:51:05 +0200 Subject: [PATCH 12/66] Improve OTA error messages adding return codes (#3698) --- esphome/components/ota/ota_backend_esp_idf.cpp | 2 +- esphome/components/ota/ota_component.cpp | 4 ++-- esphome/components/ota/ota_component.h | 1 + esphome/espota2.py | 12 ++++++++++++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/esphome/components/ota/ota_backend_esp_idf.cpp b/esphome/components/ota/ota_backend_esp_idf.cpp index 336b3798d9..167f8c059b 100644 --- a/esphome/components/ota/ota_backend_esp_idf.cpp +++ b/esphome/components/ota/ota_backend_esp_idf.cpp @@ -49,7 +49,7 @@ OTAResponseTypes IDFOTABackend::end() { this->md5_.calculate(); if (!this->md5_.equals_hex(this->expected_bin_md5_)) { this->abort(); - return OTA_RESPONSE_ERROR_UPDATE_END; + return OTA_RESPONSE_ERROR_MD5_MISMATCH; } esp_err_t err = esp_ota_end(this->update_handle_); this->update_handle_ = 0; diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index fa2605d589..25c9f2e912 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -296,7 +296,7 @@ void OTAComponent::handle_() { error_code = backend->write(buf, read); if (error_code != OTA_RESPONSE_OK) { - ESP_LOGW(TAG, "Error writing binary data to flash!"); + ESP_LOGW(TAG, "Error writing binary data to flash!, error_code: %d", error_code); goto error; // NOLINT(cppcoreguidelines-avoid-goto) } total += read; @@ -321,7 +321,7 @@ void OTAComponent::handle_() { error_code = backend->end(); if (error_code != OTA_RESPONSE_OK) { - ESP_LOGW(TAG, "Error ending OTA!"); + ESP_LOGW(TAG, "Error ending OTA!, error_code: %d", error_code); goto error; // NOLINT(cppcoreguidelines-avoid-goto) } diff --git a/esphome/components/ota/ota_component.h b/esphome/components/ota/ota_component.h index 5647d52eeb..d178805168 100644 --- a/esphome/components/ota/ota_component.h +++ b/esphome/components/ota/ota_component.h @@ -32,6 +32,7 @@ enum OTAResponseTypes { OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE = 136, OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE = 137, OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION = 138, + OTA_RESPONSE_ERROR_MD5_MISMATCH = 139, OTA_RESPONSE_ERROR_UNKNOWN = 255, }; diff --git a/esphome/espota2.py b/esphome/espota2.py index 76f3b917c9..98d6d3a0d9 100644 --- a/esphome/espota2.py +++ b/esphome/espota2.py @@ -30,6 +30,8 @@ RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG = 134 RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG = 135 RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE = 136 RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE = 137 +RESPONSE_ERROR_NO_UPDATE_PARTITION = 138 +RESPONSE_ERROR_MD5_MISMATCH = 139 RESPONSE_ERROR_UNKNOWN = 255 OTA_VERSION_1_0 = 1 @@ -150,6 +152,16 @@ def check_error(data, expect): "Error: The OTA partition on the ESP is too small. ESPHome needs to resize " "this partition, please flash over USB." ) + if dat == RESPONSE_ERROR_NO_UPDATE_PARTITION: + raise OTAError( + "Error: The OTA partition on the ESP couldn't be found. ESPHome needs to create " + "this partition, please flash over USB." + ) + if dat == RESPONSE_ERROR_MD5_MISMATCH: + raise OTAError( + "Error: Application MD5 code mismatch. Please try again " + "or flash over USB with a good quality cable." + ) if dat == RESPONSE_ERROR_UNKNOWN: raise OTAError("Unknown error from ESP") if not isinstance(expect, (list, tuple)): From 43539f2dbf4ae211fed4720c93a6c9629857ee4c Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Tue, 16 Aug 2022 15:35:44 -0700 Subject: [PATCH 13/66] Send CR also for commands for sim800l (#3719) --- esphome/components/sim800l/sim800l.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/sim800l/sim800l.cpp b/esphome/components/sim800l/sim800l.cpp index 709e241491..a935978747 100644 --- a/esphome/components/sim800l/sim800l.cpp +++ b/esphome/components/sim800l/sim800l.cpp @@ -44,6 +44,7 @@ void Sim800LComponent::send_cmd_(const std::string &message) { ESP_LOGV(TAG, "S: %s - %d", message.c_str(), this->state_); this->watch_dog_ = 0; this->write_str(message.c_str()); + this->write_byte(ASCII_CR); this->write_byte(ASCII_LF); } From 0f6dab394a773bae63d6f5b7d8c30eaaa8876c6c Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Tue, 16 Aug 2022 20:41:46 -0700 Subject: [PATCH 14/66] fix grow password setting (#3722) Co-authored-by: Samuel Sieb --- esphome/components/fingerprint_grow/fingerprint_grow.cpp | 8 ++++---- esphome/components/fingerprint_grow/fingerprint_grow.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.cpp b/esphome/components/fingerprint_grow/fingerprint_grow.cpp index 3b8c52fea2..1d6cb776b7 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.cpp +++ b/esphome/components/fingerprint_grow/fingerprint_grow.cpp @@ -51,7 +51,7 @@ void FingerprintGrowComponent::update() { void FingerprintGrowComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up Grow Fingerprint Reader..."); if (this->check_password_()) { - if (this->new_password_ != nullptr) { + if (this->new_password_ != -1) { if (this->set_password_()) return; } else { @@ -202,9 +202,9 @@ bool FingerprintGrowComponent::check_password_() { } bool FingerprintGrowComponent::set_password_() { - ESP_LOGI(TAG, "Setting new password: %d", *this->new_password_); - this->data_ = {SET_PASSWORD, (uint8_t)(*this->new_password_ >> 24), (uint8_t)(*this->new_password_ >> 16), - (uint8_t)(*this->new_password_ >> 8), (uint8_t)(*this->new_password_ & 0xFF)}; + ESP_LOGI(TAG, "Setting new password: %d", this->new_password_); + this->data_ = {SET_PASSWORD, (uint8_t)(this->new_password_ >> 24), (uint8_t)(this->new_password_ >> 16), + (uint8_t)(this->new_password_ >> 8), (uint8_t)(this->new_password_ & 0xFF)}; if (this->send_command_() == OK) { ESP_LOGI(TAG, "New password successfully set"); ESP_LOGI(TAG, "Define the new password in your configuration and reflash now"); diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.h b/esphome/components/fingerprint_grow/fingerprint_grow.h index 7ec253ff3a..e50418a768 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.h +++ b/esphome/components/fingerprint_grow/fingerprint_grow.h @@ -96,7 +96,7 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic } void set_sensing_pin(GPIOPin *sensing_pin) { this->sensing_pin_ = sensing_pin; } void set_password(uint32_t password) { this->password_ = password; } - void set_new_password(uint32_t new_password) { this->new_password_ = &new_password; } + void set_new_password(uint32_t new_password) { this->new_password_ = new_password; } void set_fingerprint_count_sensor(sensor::Sensor *fingerprint_count_sensor) { this->fingerprint_count_sensor_ = fingerprint_count_sensor; } @@ -153,7 +153,7 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic uint8_t address_[4] = {0xFF, 0xFF, 0xFF, 0xFF}; uint16_t capacity_ = 64; uint32_t password_ = 0x0; - uint32_t *new_password_{nullptr}; + uint32_t new_password_ = -1; GPIOPin *sensing_pin_{nullptr}; uint8_t enrollment_image_ = 0; uint16_t enrollment_slot_ = 0; From 5561d4eaebb34593319c6101e185c9e4c8c1b850 Mon Sep 17 00:00:00 2001 From: functionpointer Date: Wed, 17 Aug 2022 06:07:33 +0200 Subject: [PATCH 15/66] hydreon_rgxx: Support lens_bad, em_sat and temperature (#3642) --- .../components/hydreon_rgxx/binary_sensor.py | 23 +++++- .../components/hydreon_rgxx/hydreon_rgxx.cpp | 81 ++++++++++++++----- .../components/hydreon_rgxx/hydreon_rgxx.h | 12 ++- esphome/components/hydreon_rgxx/sensor.py | 15 +++- tests/test3.yaml | 4 + 5 files changed, 110 insertions(+), 25 deletions(-) diff --git a/esphome/components/hydreon_rgxx/binary_sensor.py b/esphome/components/hydreon_rgxx/binary_sensor.py index 0d489ebcb7..776be8a5d8 100644 --- a/esphome/components/hydreon_rgxx/binary_sensor.py +++ b/esphome/components/hydreon_rgxx/binary_sensor.py @@ -4,12 +4,15 @@ from esphome.components import binary_sensor from esphome.const import ( CONF_ID, DEVICE_CLASS_COLD, + DEVICE_CLASS_PROBLEM, ) from . import hydreon_rgxx_ns, HydreonRGxxComponent CONF_HYDREON_RGXX_ID = "hydreon_rgxx_id" CONF_TOO_COLD = "too_cold" +CONF_LENS_BAD = "lens_bad" +CONF_EM_SAT = "em_sat" HydreonRGxxBinarySensor = hydreon_rgxx_ns.class_( "HydreonRGxxBinaryComponent", cg.Component @@ -23,6 +26,12 @@ CONFIG_SCHEMA = cv.Schema( cv.Optional(CONF_TOO_COLD): binary_sensor.binary_sensor_schema( device_class=DEVICE_CLASS_COLD ), + cv.Optional(CONF_LENS_BAD): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + ), + cv.Optional(CONF_EM_SAT): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_PROBLEM, + ), } ) @@ -31,6 +40,14 @@ async def to_code(config): main_sensor = await cg.get_variable(config[CONF_HYDREON_RGXX_ID]) bin_component = cg.new_Pvariable(config[CONF_ID], main_sensor) await cg.register_component(bin_component, config) - if CONF_TOO_COLD in config: - tc = await binary_sensor.new_binary_sensor(config[CONF_TOO_COLD]) - cg.add(main_sensor.set_too_cold_sensor(tc)) + + mapping = { + CONF_TOO_COLD: main_sensor.set_too_cold_sensor, + CONF_LENS_BAD: main_sensor.set_lens_bad_sensor, + CONF_EM_SAT: main_sensor.set_em_sat_sensor, + } + + for key, value in mapping.items(): + if key in config: + sensor = await binary_sensor.new_binary_sensor(config[key]) + cg.add(value(sensor)) diff --git a/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp b/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp index 25d2617677..da4345e136 100644 --- a/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +++ b/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp @@ -9,6 +9,7 @@ static const int MAX_DATA_LENGTH_BYTES = 80; static const uint8_t ASCII_LF = 0x0A; #define HYDREON_RGXX_COMMA , static const char *const PROTOCOL_NAMES[] = {HYDREON_RGXX_PROTOCOL_LIST(, HYDREON_RGXX_COMMA)}; +static const char *const IGNORE_STRINGS[] = {HYDREON_RGXX_IGNORE_LIST(, HYDREON_RGXX_COMMA)}; void HydreonRGxxComponent::dump_config() { this->check_uart_settings(9600, 1, esphome::uart::UART_CONFIG_PARITY_NONE, 8); @@ -34,33 +35,37 @@ void HydreonRGxxComponent::setup() { this->schedule_reboot_(); } -bool HydreonRGxxComponent::sensor_missing_() { +int HydreonRGxxComponent::num_sensors_missing_() { if (this->sensors_received_ == -1) { - // no request sent yet, don't check - return false; - } else { - if (this->sensors_received_ == 0) { - ESP_LOGW(TAG, "No data at all"); - return true; - } - for (int i = 0; i < NUM_SENSORS; i++) { - if (this->sensors_[i] == nullptr) { - continue; - } - if ((this->sensors_received_ >> i & 1) == 0) { - ESP_LOGW(TAG, "Missing %s", PROTOCOL_NAMES[i]); - return true; - } - } - return false; + return -1; } + int ret = NUM_SENSORS; + for (int i = 0; i < NUM_SENSORS; i++) { + if (this->sensors_[i] == nullptr) { + ret -= 1; + continue; + } + if ((this->sensors_received_ >> i & 1) != 0) { + ret -= 1; + } + } + return ret; } void HydreonRGxxComponent::update() { if (this->boot_count_ > 0) { - if (this->sensor_missing_()) { + if (this->num_sensors_missing_() > 0) { + for (int i = 0; i < NUM_SENSORS; i++) { + if (this->sensors_[i] == nullptr) { + continue; + } + if ((this->sensors_received_ >> i & 1) == 0) { + ESP_LOGW(TAG, "Missing %s", PROTOCOL_NAMES[i]); + } + } + this->no_response_count_++; - ESP_LOGE(TAG, "data missing %d times", this->no_response_count_); + ESP_LOGE(TAG, "missing %d sensors; %d times in a row", this->num_sensors_missing_(), this->no_response_count_); if (this->no_response_count_ > 15) { ESP_LOGE(TAG, "asking sensor to reboot"); for (auto &sensor : this->sensors_) { @@ -79,8 +84,16 @@ void HydreonRGxxComponent::update() { if (this->too_cold_sensor_ != nullptr) { this->too_cold_sensor_->publish_state(this->too_cold_); } + if (this->lens_bad_sensor_ != nullptr) { + this->lens_bad_sensor_->publish_state(this->lens_bad_); + } + if (this->em_sat_sensor_ != nullptr) { + this->em_sat_sensor_->publish_state(this->em_sat_); + } #endif this->too_cold_ = false; + this->lens_bad_ = false; + this->em_sat_ = false; this->sensors_received_ = 0; } } @@ -146,6 +159,25 @@ void HydreonRGxxComponent::process_line_() { ESP_LOGI(TAG, "Comment: %s", this->buffer_.substr(0, this->buffer_.size() - 2).c_str()); return; } + std::string::size_type newlineposn = this->buffer_.find('\n'); + if (newlineposn <= 1) { + // allow both \r\n and \n + ESP_LOGD(TAG, "Received empty line"); + return; + } + if (newlineposn <= 2) { + // single character lines, such as acknowledgements + ESP_LOGD(TAG, "Received ack: %s", this->buffer_.substr(0, this->buffer_.size() - 2).c_str()); + return; + } + if (this->buffer_.find("LensBad") != std::string::npos) { + ESP_LOGW(TAG, "Received LensBad!"); + this->lens_bad_ = true; + } + if (this->buffer_.find("EmSat") != std::string::npos) { + ESP_LOGW(TAG, "Received EmSat!"); + this->em_sat_ = true; + } if (this->buffer_starts_with_("PwrDays")) { if (this->boot_count_ <= 0) { this->boot_count_ = 1; @@ -200,7 +232,16 @@ void HydreonRGxxComponent::process_line_() { ESP_LOGD(TAG, "Received %s: %f", PROTOCOL_NAMES[i], this->sensors_[i]->get_raw_state()); this->sensors_received_ |= (1 << i); } + if (this->request_temperature_ && this->num_sensors_missing_() == 1) { + this->write_str("T\n"); + } } else { + for (const auto *ignore : IGNORE_STRINGS) { + if (this->buffer_starts_with_(ignore)) { + ESP_LOGI(TAG, "Ignoring %s", this->buffer_.substr(0, this->buffer_.size() - 2).c_str()); + return; + } + } ESP_LOGI(TAG, "Got unknown line: %s", this->buffer_.c_str()); } } diff --git a/esphome/components/hydreon_rgxx/hydreon_rgxx.h b/esphome/components/hydreon_rgxx/hydreon_rgxx.h index ebe4a35b19..1697060d28 100644 --- a/esphome/components/hydreon_rgxx/hydreon_rgxx.h +++ b/esphome/components/hydreon_rgxx/hydreon_rgxx.h @@ -26,13 +26,18 @@ static const uint8_t NUM_SENSORS = 1; #define HYDREON_RGXX_PROTOCOL_LIST(F, SEP) F("") #endif +#define HYDREON_RGXX_IGNORE_LIST(F, SEP) F("Emitters") SEP F("Event") SEP F("Reset") + class HydreonRGxxComponent : public PollingComponent, public uart::UARTDevice { public: void set_sensor(sensor::Sensor *sensor, int index) { this->sensors_[index] = sensor; } #ifdef USE_BINARY_SENSOR void set_too_cold_sensor(binary_sensor::BinarySensor *sensor) { this->too_cold_sensor_ = sensor; } + void set_lens_bad_sensor(binary_sensor::BinarySensor *sensor) { this->lens_bad_sensor_ = sensor; } + void set_em_sat_sensor(binary_sensor::BinarySensor *sensor) { this->em_sat_sensor_ = sensor; } #endif void set_model(RGModel model) { model_ = model; } + void set_request_temperature(bool b) { request_temperature_ = b; } /// Schedule data readings. void update() override; @@ -49,11 +54,13 @@ class HydreonRGxxComponent : public PollingComponent, public uart::UARTDevice { void schedule_reboot_(); bool buffer_starts_with_(const std::string &prefix); bool buffer_starts_with_(const char *prefix); - bool sensor_missing_(); + int num_sensors_missing_(); sensor::Sensor *sensors_[NUM_SENSORS] = {nullptr}; #ifdef USE_BINARY_SENSOR binary_sensor::BinarySensor *too_cold_sensor_ = nullptr; + binary_sensor::BinarySensor *lens_bad_sensor_ = nullptr; + binary_sensor::BinarySensor *em_sat_sensor_ = nullptr; #endif int16_t boot_count_ = 0; @@ -62,6 +69,9 @@ class HydreonRGxxComponent : public PollingComponent, public uart::UARTDevice { RGModel model_ = RG9; int sw_version_ = 0; bool too_cold_ = false; + bool lens_bad_ = false; + bool em_sat_ = false; + bool request_temperature_ = false; // bit field showing which sensors we have received data for int sensors_received_ = -1; diff --git a/esphome/components/hydreon_rgxx/sensor.py b/esphome/components/hydreon_rgxx/sensor.py index c604f8d3c1..730499a493 100644 --- a/esphome/components/hydreon_rgxx/sensor.py +++ b/esphome/components/hydreon_rgxx/sensor.py @@ -5,8 +5,11 @@ from esphome.const import ( CONF_ID, CONF_MODEL, CONF_MOISTURE, + CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + ICON_THERMOMETER, ) from . import RGModel, HydreonRGxxComponent @@ -33,6 +36,7 @@ SUPPORTED_SENSORS = { CONF_TOTAL_ACC: ["RG_15"], CONF_R_INT: ["RG_15"], CONF_MOISTURE: ["RG_9"], + CONF_TEMPERATURE: ["RG_9"], } PROTOCOL_NAMES = { CONF_MOISTURE: "R", @@ -40,6 +44,7 @@ PROTOCOL_NAMES = { CONF_R_INT: "RInt", CONF_EVENT_ACC: "EventAcc", CONF_TOTAL_ACC: "TotalAcc", + CONF_TEMPERATURE: "t", } @@ -92,6 +97,12 @@ CONFIG_SCHEMA = cv.All( device_class=DEVICE_CLASS_HUMIDITY, state_class=STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=0, + icon=ICON_THERMOMETER, + state_class=STATE_CLASS_MEASUREMENT, + ), } ) .extend(cv.polling_component_schema("60s")) @@ -108,7 +119,7 @@ async def to_code(config): cg.add_define( "HYDREON_RGXX_PROTOCOL_LIST(F, sep)", cg.RawExpression( - " sep ".join([f'F("{name}")' for name in PROTOCOL_NAMES.values()]) + " sep ".join([f'F("{name} ")' for name in PROTOCOL_NAMES.values()]) ), ) cg.add_define("HYDREON_RGXX_NUM_SENSORS", len(PROTOCOL_NAMES)) @@ -117,3 +128,5 @@ async def to_code(config): if conf in config: sens = await sensor.new_sensor(config[conf]) cg.add(var.set_sensor(sens, i)) + + cg.add(var.set_request_temperature(CONF_TEMPERATURE in config)) diff --git a/tests/test3.yaml b/tests/test3.yaml index 1abbee8dc5..4b2224dc3c 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -866,6 +866,10 @@ binary_sensor: hydreon_rgxx_id: "hydreon_rg9" too_cold: name: "rg9_toocold" + em_sat: + name: "rg9_emsat" + lens_bad: + name: "rg9_lens_bad" - platform: template id: 'pzemac_reset_energy' on_press: From b4bbe3d7b557341881612daafd0a28b8bca70bbd Mon Sep 17 00:00:00 2001 From: Stijn Tintel Date: Wed, 17 Aug 2022 12:10:43 +0300 Subject: [PATCH 16/66] wifi: support 802.11k and 802.11v (#3600) --- esphome/components/wifi/__init__.py | 19 ++++++++++++++++++- esphome/components/wifi/wifi_component.cpp | 8 ++++++++ esphome/components/wifi/wifi_component.h | 9 +++++++++ .../wifi/wifi_component_esp_idf.cpp | 5 +++++ esphome/const.py | 2 ++ esphome/core/defines.h | 1 + 6 files changed, 43 insertions(+), 1 deletion(-) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index c3f70506e2..846a8e1303 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -10,6 +10,8 @@ from esphome.const import ( CONF_DNS1, CONF_DNS2, CONF_DOMAIN, + CONF_ENABLE_BTM, + CONF_ENABLE_RRM, CONF_FAST_CONNECT, CONF_GATEWAY, CONF_HIDDEN, @@ -32,10 +34,10 @@ from esphome.const import ( CONF_EAP, ) from esphome.core import CORE, HexInt, coroutine_with_priority +from esphome.components.esp32 import add_idf_sdkconfig_option from esphome.components.network import IPAddress from . import wpa2_eap - AUTO_LOAD = ["network"] wifi_ns = cg.esphome_ns.namespace("wifi") @@ -272,6 +274,12 @@ CONFIG_SCHEMA = cv.All( cv.SplitDefault(CONF_OUTPUT_POWER, esp8266=20.0): cv.All( cv.decibel, cv.float_range(min=8.5, max=20.5) ), + cv.SplitDefault(CONF_ENABLE_BTM, esp32_idf=False): cv.All( + cv.boolean, cv.only_with_esp_idf + ), + cv.SplitDefault(CONF_ENABLE_RRM, esp32_idf=False): cv.All( + cv.boolean, cv.only_with_esp_idf + ), cv.Optional("enable_mdns"): cv.invalid( "This option has been removed. Please use the [disabled] option under the " "new mdns component instead." @@ -373,6 +381,15 @@ async def to_code(config): elif CORE.is_esp32 and CORE.using_arduino: cg.add_library("WiFi", None) + if CORE.is_esp32 and CORE.using_esp_idf: + if config[CONF_ENABLE_BTM] or config[CONF_ENABLE_RRM]: + add_idf_sdkconfig_option("CONFIG_WPA_11KV_SUPPORT", True) + cg.add_define("USE_WIFI_11KV_SUPPORT") + if config[CONF_ENABLE_BTM]: + cg.add(var.set_btm(config[CONF_ENABLE_BTM])) + if config[CONF_ENABLE_RRM]: + cg.add(var.set_rrm(config[CONF_ENABLE_RRM])) + cg.add_define("USE_WIFI") # Register at end for OTA safe mode diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 0ac227cd82..10bf4a8f9c 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -166,6 +166,10 @@ WiFiComponent::WiFiComponent() { global_wifi_component = this; } bool WiFiComponent::has_ap() const { return this->has_ap_; } bool WiFiComponent::has_sta() const { return !this->sta_.empty(); } void WiFiComponent::set_fast_connect(bool fast_connect) { this->fast_connect_ = fast_connect; } +#ifdef USE_WIFI_11KV_SUPPORT +void WiFiComponent::set_btm(bool btm) { this->btm_ = btm; } +void WiFiComponent::set_rrm(bool rrm) { this->rrm_ = rrm; } +#endif network::IPAddress WiFiComponent::get_ip_address() { if (this->has_sta()) return this->wifi_sta_ip(); @@ -366,6 +370,10 @@ void WiFiComponent::print_connect_params_() { ESP_LOGCONFIG(TAG, " Gateway: %s", wifi_gateway_ip_().str().c_str()); ESP_LOGCONFIG(TAG, " DNS1: %s", wifi_dns_ip_(0).str().c_str()); ESP_LOGCONFIG(TAG, " DNS2: %s", wifi_dns_ip_(1).str().c_str()); +#ifdef USE_WIFI_11KV_SUPPORT + ESP_LOGCONFIG(TAG, " BTM: %s", this->btm_ ? "enabled" : "disabled"); + ESP_LOGCONFIG(TAG, " RRM: %s", this->rrm_ ? "enabled" : "disabled"); +#endif } void WiFiComponent::start_scanning() { diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 7941e7f643..6c5202ed7a 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -219,6 +219,11 @@ class WiFiComponent : public Component { bool has_sta() const; bool has_ap() const; +#ifdef USE_WIFI_11KV_SUPPORT + void set_btm(bool btm); + void set_rrm(bool rrm); +#endif + network::IPAddress get_ip_address(); std::string get_use_address() const; void set_use_address(const std::string &use_address); @@ -327,6 +332,10 @@ class WiFiComponent : public Component { optional output_power_; ESPPreferenceObject pref_; bool has_saved_wifi_settings_{false}; +#ifdef USE_WIFI_11KV_SUPPORT + bool btm_{false}; + bool rrm_{false}; +#endif }; extern WiFiComponent *global_wifi_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 6d20219c69..2883164495 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -285,6 +285,11 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { } #endif +#ifdef USE_WIFI_11KV_SUPPORT + conf.sta.btm_enabled = this->btm_; + conf.sta.rm_enabled = this->rrm_; +#endif + if (ap.get_bssid().has_value()) { conf.sta.bssid_set = true; memcpy(conf.sta.bssid, ap.get_bssid()->data(), 6); diff --git a/esphome/const.py b/esphome/const.py index b3e038fe20..7735e96661 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -197,8 +197,10 @@ CONF_ECO2 = "eco2" CONF_EFFECT = "effect" CONF_EFFECTS = "effects" CONF_ELSE = "else" +CONF_ENABLE_BTM = "enable_btm" CONF_ENABLE_IPV6 = "enable_ipv6" CONF_ENABLE_PIN = "enable_pin" +CONF_ENABLE_RRM = "enable_rrm" CONF_ENABLE_TIME = "enable_time" CONF_ENERGY = "energy" CONF_ENTITY_CATEGORY = "entity_category" diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 5b1d567aa4..727b35e6f9 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -70,6 +70,7 @@ #define USE_ESP32_IGNORE_EFUSE_MAC_CRC #define USE_IMPROV #define USE_SOCKET_IMPL_BSD_SOCKETS +#define USE_WIFI_11KV_SUPPORT #ifdef USE_ARDUINO #define USE_ARDUINO_VERSION_CODE VERSION_CODE(1, 0, 6) From d546ef941feb90ed567d7b23d8a75d488ea2e77a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 18 Aug 2022 11:04:50 +1200 Subject: [PATCH 17/66] Add final validate for i2c with mix/max frequency (#3727) Co-authored-by: MrEditor97 --- esphome/components/i2c/__init__.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/esphome/components/i2c/__init__.py b/esphome/components/i2c/__init__.py index 46f0abacc6..a04e63e789 100644 --- a/esphome/components/i2c/__init__.py +++ b/esphome/components/i2c/__init__.py @@ -1,5 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv +import esphome.final_validate as fv from esphome import pins from esphome.const import ( CONF_FREQUENCY, @@ -110,3 +111,27 @@ async def register_i2c_device(var, config): parent = await cg.get_variable(config[CONF_I2C_ID]) cg.add(var.set_i2c_bus(parent)) cg.add(var.set_i2c_address(config[CONF_ADDRESS])) + + +def final_validate_device_schema( + name: str, *, min_frequency: cv.frequency = None, max_frequency: cv.frequency = None +): + hub_schema = {} + if min_frequency is not None: + hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range( + min=cv.frequency(min_frequency), + min_included=True, + msg=f"Component {name} requires a minimum frequency of {min_frequency} for the I2C bus", + ) + + if max_frequency is not None: + hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range( + max=cv.frequency(max_frequency), + max_included=True, + msg=f"Component {name} cannot be used with a frequency of over {max_frequency} for the I2C bus", + ) + + return cv.Schema( + {cv.Required(CONF_I2C_ID): fv.id_declaration_match_schema(hub_schema)}, + extra=cv.ALLOW_EXTRA, + ) From fc15ddfa91154edfc7a5f23c80cbcd5233d8ee5f Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Thu, 18 Aug 2022 14:49:35 -0500 Subject: [PATCH 18/66] Add dps310 sensor support (#3704) Co-authored-by: Greg Arnold --- CODEOWNERS | 1 + esphome/components/dps310/__init__.py | 0 esphome/components/dps310/dps310.cpp | 189 ++++++++++++++++++++++++++ esphome/components/dps310/dps310.h | 65 +++++++++ esphome/components/dps310/sensor.py | 62 +++++++++ tests/test1.yaml | 8 ++ 6 files changed, 325 insertions(+) create mode 100644 esphome/components/dps310/__init__.py create mode 100644 esphome/components/dps310/dps310.cpp create mode 100644 esphome/components/dps310/dps310.h create mode 100644 esphome/components/dps310/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index 62aae6d3cb..7b5fa93a3c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -59,6 +59,7 @@ esphome/components/debug/* @OttoWinter esphome/components/delonghi/* @grob6000 esphome/components/dfplayer/* @glmnet esphome/components/dht/* @OttoWinter +esphome/components/dps310/* @kbx81 esphome/components/ds1307/* @badbadc0ffee esphome/components/dsmr/* @glmnet @zuidwijk esphome/components/ektf2232/* @jesserockz diff --git a/esphome/components/dps310/__init__.py b/esphome/components/dps310/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/dps310/dps310.cpp b/esphome/components/dps310/dps310.cpp new file mode 100644 index 0000000000..22fb52967f --- /dev/null +++ b/esphome/components/dps310/dps310.cpp @@ -0,0 +1,189 @@ +#include "dps310.h" +#include "esphome/core/log.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace dps310 { + +static const char *const TAG = "dps310"; + +void DPS310Component::setup() { + uint8_t coef_data_raw[DPS310_NUM_COEF_REGS]; + auto timer = DPS310_INIT_TIMEOUT; + uint8_t reg = 0; + + ESP_LOGCONFIG(TAG, "Setting up DPS310..."); + // first, reset the sensor + if (!this->write_byte(DPS310_REG_RESET, DPS310_CMD_RESET)) { + this->mark_failed(); + return; + } + delay(10); + // wait for the sensor and its coefficients to be ready + while (timer-- && (!(reg & DPS310_BIT_SENSOR_RDY) || !(reg & DPS310_BIT_COEF_RDY))) { + reg = this->read_byte(DPS310_REG_MEAS_CFG).value_or(0); + delay(5); + } + + if (!(reg & DPS310_BIT_SENSOR_RDY) || !(reg & DPS310_BIT_COEF_RDY)) { // the flags were not set in time + this->mark_failed(); + return; + } + // read device ID + if (!this->read_byte(DPS310_REG_PROD_REV_ID, &this->prod_rev_id_)) { + this->mark_failed(); + return; + } + // read in coefficients used to calculate the compensated pressure and temperature values + if (!this->read_bytes(DPS310_REG_COEF, coef_data_raw, DPS310_NUM_COEF_REGS)) { + this->mark_failed(); + return; + } + // read in coefficients source register, too -- we need this a few lines down + if (!this->read_byte(DPS310_REG_TMP_COEF_SRC, ®)) { + this->mark_failed(); + return; + } + // set up operational stuff + if (!this->write_byte(DPS310_REG_PRS_CFG, DPS310_VAL_PRS_CFG)) { + this->mark_failed(); + return; + } + if (!this->write_byte(DPS310_REG_TMP_CFG, DPS310_VAL_TMP_CFG | (reg & DPS310_BIT_TMP_COEF_SRC))) { + this->mark_failed(); + return; + } + if (!this->write_byte(DPS310_REG_CFG, DPS310_VAL_REG_CFG)) { + this->mark_failed(); + return; + } + if (!this->write_byte(DPS310_REG_MEAS_CFG, 0x07)) { // enable background mode + this->mark_failed(); + return; + } + + this->c0_ = // we only ever use c0/2, so just divide by 2 here to save time later + DPS310Component::twos_complement( + int16_t(((uint16_t) coef_data_raw[0] << 4) | (((uint16_t) coef_data_raw[1] >> 4) & 0x0F)), 12) / + 2; + + this->c1_ = + DPS310Component::twos_complement(int16_t((((uint16_t) coef_data_raw[1] & 0x0F) << 8) | coef_data_raw[2]), 12); + + this->c00_ = ((uint32_t) coef_data_raw[3] << 12) | ((uint32_t) coef_data_raw[4] << 4) | + (((uint32_t) coef_data_raw[5] >> 4) & 0x0F); + this->c00_ = DPS310Component::twos_complement(c00_, 20); + + this->c10_ = + (((uint32_t) coef_data_raw[5] & 0x0F) << 16) | ((uint32_t) coef_data_raw[6] << 8) | (uint32_t) coef_data_raw[7]; + this->c10_ = DPS310Component::twos_complement(c10_, 20); + + this->c01_ = int16_t(((uint16_t) coef_data_raw[8] << 8) | (uint16_t) coef_data_raw[9]); + this->c11_ = int16_t(((uint16_t) coef_data_raw[10] << 8) | (uint16_t) coef_data_raw[11]); + this->c20_ = int16_t(((uint16_t) coef_data_raw[12] << 8) | (uint16_t) coef_data_raw[13]); + this->c21_ = int16_t(((uint16_t) coef_data_raw[14] << 8) | (uint16_t) coef_data_raw[15]); + this->c30_ = int16_t(((uint16_t) coef_data_raw[16] << 8) | (uint16_t) coef_data_raw[17]); +} + +void DPS310Component::dump_config() { + ESP_LOGCONFIG(TAG, "DPS310:"); + ESP_LOGCONFIG(TAG, " Product ID: %u", this->prod_rev_id_ & 0x0F); + ESP_LOGCONFIG(TAG, " Revision ID: %u", (this->prod_rev_id_ >> 4) & 0x0F); + LOG_I2C_DEVICE(this); + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication with DPS310 failed!"); + } + LOG_UPDATE_INTERVAL(this); + LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); + LOG_SENSOR(" ", "Pressure", this->pressure_sensor_); +} + +float DPS310Component::get_setup_priority() const { return setup_priority::DATA; } + +void DPS310Component::update() { + if (!this->update_in_progress_) { + this->update_in_progress_ = true; + this->read_(); + } +} + +void DPS310Component::read_() { + uint8_t reg = 0; + if (!this->read_byte(DPS310_REG_MEAS_CFG, ®)) { + this->status_set_warning(); + return; + } + + if ((!this->got_pres_) && (reg & DPS310_BIT_PRS_RDY)) { + this->read_pressure_(); + } + + if ((!this->got_temp_) && (reg & DPS310_BIT_TMP_RDY)) { + this->read_temperature_(); + } + + if (this->got_pres_ && this->got_temp_) { + this->calculate_values_(this->raw_temperature_, this->raw_pressure_); + this->got_pres_ = false; + this->got_temp_ = false; + this->update_in_progress_ = false; + this->status_clear_warning(); + } else { + auto f = std::bind(&DPS310Component::read_, this); + this->set_timeout("dps310", 10, f); + } +} + +void DPS310Component::read_pressure_() { + uint8_t bytes[3]; + if (!this->read_bytes(DPS310_REG_PRS_B2, bytes, 3)) { + this->status_set_warning(); + return; + } + this->got_pres_ = true; + this->raw_pressure_ = DPS310Component::twos_complement( + int32_t((uint32_t(bytes[0]) << 16) | (uint32_t(bytes[1]) << 8) | (uint32_t(bytes[2]))), 24); +} + +void DPS310Component::read_temperature_() { + uint8_t bytes[3]; + if (!this->read_bytes(DPS310_REG_TMP_B2, bytes, 3)) { + this->status_set_warning(); + return; + } + this->got_temp_ = true; + this->raw_temperature_ = DPS310Component::twos_complement( + int32_t((uint32_t(bytes[0]) << 16) | (uint32_t(bytes[1]) << 8) | (uint32_t(bytes[2]))), 24); +} + +// Calculations are taken from the datasheet which can be found here: +// https://www.infineon.com/dgdl/Infineon-DPS310-DataSheet-v01_02-EN.pdf?fileId=5546d462576f34750157750826c42242 +// Sections "How to Calculate Compensated Pressure Values" and "How to Calculate Compensated Temperature Values" +// Variable names below match variable names from the datasheet but lowercased +void DPS310Component::calculate_values_(int32_t raw_temperature, int32_t raw_pressure) { + const float t_raw_sc = (float) raw_temperature / DPS310_SCALE_FACTOR; + const float p_raw_sc = (float) raw_pressure / DPS310_SCALE_FACTOR; + + const float temperature = t_raw_sc * this->c1_ + this->c0_; // c0/2 done earlier! + + const float pressure = (this->c00_ + p_raw_sc * (this->c10_ + p_raw_sc * (this->c20_ + p_raw_sc * this->c30_)) + + t_raw_sc * this->c01_ + t_raw_sc * p_raw_sc * (this->c11_ + p_raw_sc * this->c21_)) / + 100; // divide by 100 for hPa + + if (this->temperature_sensor_ != nullptr) { + this->temperature_sensor_->publish_state(temperature); + } + if (this->pressure_sensor_ != nullptr) { + this->pressure_sensor_->publish_state(pressure); + } +} + +int32_t DPS310Component::twos_complement(int32_t val, uint8_t bits) { + if (val & ((uint32_t) 1 << (bits - 1))) { + val -= (uint32_t) 1 << bits; + } + return val; +} + +} // namespace dps310 +} // namespace esphome diff --git a/esphome/components/dps310/dps310.h b/esphome/components/dps310/dps310.h new file mode 100644 index 0000000000..7aca2c3d10 --- /dev/null +++ b/esphome/components/dps310/dps310.h @@ -0,0 +1,65 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace dps310 { + +static const uint8_t DPS310_REG_PRS_B2 = 0x00; // Highest byte of pressure data +static const uint8_t DPS310_REG_TMP_B2 = 0x03; // Highest byte of temperature data +static const uint8_t DPS310_REG_PRS_CFG = 0x06; // Pressure configuration +static const uint8_t DPS310_REG_TMP_CFG = 0x07; // Temperature configuration +static const uint8_t DPS310_REG_MEAS_CFG = 0x08; // Sensor configuration +static const uint8_t DPS310_REG_CFG = 0x09; // Interrupt/FIFO configuration +static const uint8_t DPS310_REG_RESET = 0x0C; // Soft reset +static const uint8_t DPS310_REG_PROD_REV_ID = 0x0D; // Register that contains the part ID +static const uint8_t DPS310_REG_COEF = 0x10; // Top of calibration coefficient data space +static const uint8_t DPS310_REG_TMP_COEF_SRC = 0x28; // Temperature calibration src + +static const uint8_t DPS310_BIT_PRS_RDY = 0x10; // Pressure measurement is ready +static const uint8_t DPS310_BIT_TMP_RDY = 0x20; // Temperature measurement is ready +static const uint8_t DPS310_BIT_SENSOR_RDY = 0x40; // Sensor initialization complete when bit is set +static const uint8_t DPS310_BIT_COEF_RDY = 0x80; // Coefficients are available when bit is set +static const uint8_t DPS310_BIT_TMP_COEF_SRC = 0x80; // Temperature measurement source (0 = ASIC, 1 = MEMS element) +static const uint8_t DPS310_BIT_REQ_PRES = 0x01; // Set this bit to request pressure reading +static const uint8_t DPS310_BIT_REQ_TEMP = 0x02; // Set this bit to request temperature reading + +static const uint8_t DPS310_CMD_RESET = 0x89; // What to write to reset the device + +static const uint8_t DPS310_VAL_PRS_CFG = 0x01; // Value written to DPS310_REG_PRS_CFG at startup +static const uint8_t DPS310_VAL_TMP_CFG = 0x01; // Value written to DPS310_REG_TMP_CFG at startup +static const uint8_t DPS310_VAL_REG_CFG = 0x00; // Value written to DPS310_REG_CFG at startup + +static const uint8_t DPS310_INIT_TIMEOUT = 20; // How long to wait for DPS310 start-up to complete +static const uint8_t DPS310_NUM_COEF_REGS = 18; // Number of coefficients we need to read from the device +static const int32_t DPS310_SCALE_FACTOR = 1572864; // Measurement compensation scale factor + +class DPS310Component : public PollingComponent, public i2c::I2CDevice { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override; + void update() override; + + void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } + void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; } + + protected: + void read_(); + void read_pressure_(); + void read_temperature_(); + void calculate_values_(int32_t raw_temperature, int32_t raw_pressure); + static int32_t twos_complement(int32_t val, uint8_t bits); + + sensor::Sensor *temperature_sensor_; + sensor::Sensor *pressure_sensor_; + int32_t raw_pressure_, raw_temperature_, c00_, c10_; + int16_t c0_, c1_, c01_, c11_, c20_, c21_, c30_; + uint8_t prod_rev_id_; + bool got_pres_, got_temp_, update_in_progress_; +}; + +} // namespace dps310 +} // namespace esphome diff --git a/esphome/components/dps310/sensor.py b/esphome/components/dps310/sensor.py new file mode 100644 index 0000000000..742c873d9e --- /dev/null +++ b/esphome/components/dps310/sensor.py @@ -0,0 +1,62 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, sensor +from esphome.const import ( + CONF_ID, + CONF_PRESSURE, + CONF_TEMPERATURE, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + ICON_GAUGE, + ICON_THERMOMETER, + UNIT_HECTOPASCAL, +) + +CODEOWNERS = ["@kbx81"] + +DEPENDENCIES = ["i2c"] + +dps310_ns = cg.esphome_ns.namespace("dps310") +DPS310Component = dps310_ns.class_( + "DPS310Component", cg.PollingComponent, i2c.I2CDevice +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(DPS310Component), + cv.Required(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + icon=ICON_THERMOMETER, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Required(CONF_PRESSURE): sensor.sensor_schema( + unit_of_measurement=UNIT_HECTOPASCAL, + icon=ICON_GAUGE, + accuracy_decimals=1, + device_class=DEVICE_CLASS_PRESSURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x77)) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + + if CONF_TEMPERATURE in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE]) + cg.add(var.set_temperature_sensor(sens)) + + if CONF_PRESSURE in config: + sens = await sensor.new_sensor(config[CONF_PRESSURE]) + cg.add(var.set_pressure_sensor(sens)) diff --git a/tests/test1.yaml b/tests/test1.yaml index 387aab6bbe..5bd406a10f 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -768,6 +768,14 @@ sensor: temperature: name: "MPU6886 Temperature" i2c_id: i2c_bus + - platform: dps310 + temperature: + name: "DPS310 Temperature" + pressure: + name: "DPS310 Pressure" + address: 0x77 + update_interval: 15s + i2c_id: i2c_bus - platform: ms5611 temperature: name: "Outside Temperature" From a47e92f2bccfa249c351ce719738d402a059ea17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20L=C3=B6vdahl?= Date: Thu, 18 Aug 2022 22:54:10 +0300 Subject: [PATCH 19/66] Let favicon be cached (#3729) --- esphome/dashboard/dashboard.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 1fadac968d..ca2767639d 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -947,9 +947,12 @@ def make_app(debug=get_bool_env(ENV_DEV)): class StaticFileHandler(tornado.web.StaticFileHandler): def set_extra_headers(self, path): - self.set_header( - "Cache-Control", "no-store, no-cache, must-revalidate, max-age=0" - ) + if "favicon.ico" in path: + self.set_header("Cache-Control", "max-age=84600, public") + else: + self.set_header( + "Cache-Control", "no-store, no-cache, must-revalidate, max-age=0" + ) app_settings = { "debug": debug, From ac3cdf487fcce1a4815781a5691a632e3938392b Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Thu, 18 Aug 2022 14:55:10 -0500 Subject: [PATCH 20/66] Fix SPI HW selection for ESP32 variants (#3728) --- esphome/components/spi/spi.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index 95280f23b9..864f6ae39d 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -76,7 +76,11 @@ void SPIComponent::setup() { if (spi_bus_num == 0) { this->hw_spi_ = &SPI; } else { +#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) + this->hw_spi_ = new SPIClass(FSPI); // NOLINT(cppcoreguidelines-owning-memory) +#else this->hw_spi_ = new SPIClass(VSPI); // NOLINT(cppcoreguidelines-owning-memory) +#endif // USE_ESP32_VARIANT } spi_bus_num++; this->hw_spi_->begin(clk_pin, miso_pin, mosi_pin); From 3d0a85ee78b2461aa9264ad88ebd56d3e9396e39 Mon Sep 17 00:00:00 2001 From: Mike Ryan Date: Thu, 18 Aug 2022 15:49:52 -0700 Subject: [PATCH 21/66] Add bitmap font support (#3573) Co-authored-by: Otto Winter --- esphome/components/font/__init__.py | 142 +++++++++++++++++++++++++--- 1 file changed, 130 insertions(+), 12 deletions(-) diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index 9317b2ec94..ced433dc5a 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -1,6 +1,7 @@ import functools from pathlib import Path import hashlib +import os import re import requests @@ -9,6 +10,7 @@ from esphome import core from esphome.components import display import esphome.config_validation as cv import esphome.codegen as cg +from esphome.helpers import copy_file_if_changed from esphome.const import ( CONF_FAMILY, CONF_FILE, @@ -88,21 +90,33 @@ def validate_truetype_file(value): return cv.file_(value) -def _compute_gfonts_local_path(value) -> Path: - name = f"{value[CONF_FAMILY]}@{value[CONF_WEIGHT]}@{value[CONF_ITALIC]}@v1" +def _compute_local_font_dir(name) -> Path: base_dir = Path(CORE.config_dir) / ".esphome" / DOMAIN h = hashlib.new("sha256") h.update(name.encode()) - return base_dir / h.hexdigest()[:8] / "font.ttf" + return base_dir / h.hexdigest()[:8] + + +def _compute_gfonts_local_path(value) -> Path: + name = f"{value[CONF_FAMILY]}@{value[CONF_WEIGHT]}@{value[CONF_ITALIC]}@v1" + return _compute_local_font_dir(name) / "font.ttf" TYPE_LOCAL = "local" +TYPE_LOCAL_BITMAP = "local_bitmap" TYPE_GFONTS = "gfonts" LOCAL_SCHEMA = cv.Schema( { cv.Required(CONF_PATH): validate_truetype_file, } ) + +LOCAL_BITMAP_SCHEMA = cv.Schema( + { + cv.Required(CONF_PATH): cv.file_, + } +) + CONF_ITALIC = "italic" FONT_WEIGHTS = { "thin": 100, @@ -185,6 +199,15 @@ def validate_file_shorthand(value): if weight is not None: data[CONF_WEIGHT] = weight[1:] return FILE_SCHEMA(data) + + if value.endswith(".pcf") or value.endswith(".bdf"): + return FILE_SCHEMA( + { + CONF_TYPE: TYPE_LOCAL_BITMAP, + CONF_PATH: value, + } + ) + return FILE_SCHEMA( { CONF_TYPE: TYPE_LOCAL, @@ -197,6 +220,7 @@ TYPED_FILE_SCHEMA = cv.typed_schema( { TYPE_LOCAL: LOCAL_SCHEMA, TYPE_GFONTS: GFONTS_SCHEMA, + TYPE_LOCAL_BITMAP: LOCAL_BITMAP_SCHEMA, } ) @@ -228,27 +252,121 @@ FONT_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.All(validate_pillow_installed, FONT_SCHEMA) +# PIL doesn't provide a consistent interface for both TrueType and bitmap +# fonts. So, we use our own wrappers to give us the consistency that we need. -async def to_code(config): + +class TrueTypeFontWrapper: + def __init__(self, font): + self.font = font + + def getoffset(self, glyph): + _, (offset_x, offset_y) = self.font.font.getsize(glyph) + return offset_x, offset_y + + def getmask(self, glyph, **kwargs): + return self.font.getmask(glyph, **kwargs) + + def getmetrics(self, glyphs): + return self.font.getmetrics() + + +class BitmapFontWrapper: + def __init__(self, font): + self.font = font + self.max_height = 0 + + def getoffset(self, glyph): + return 0, 0 + + def getmask(self, glyph, **kwargs): + return self.font.getmask(glyph, **kwargs) + + def getmetrics(self, glyphs): + max_height = 0 + for glyph in glyphs: + mask = self.getmask(glyph, mode="1") + _, height = mask.size + if height > max_height: + max_height = height + return (max_height, 0) + + +def convert_bitmap_to_pillow_font(filepath): + from PIL import PcfFontFile, BdfFontFile + + local_bitmap_font_file = _compute_local_font_dir(filepath) / os.path.basename( + filepath + ) + + copy_file_if_changed(filepath, local_bitmap_font_file) + + with open(local_bitmap_font_file, "rb") as fp: + try: + try: + p = PcfFontFile.PcfFontFile(fp) + except SyntaxError: + fp.seek(0) + p = BdfFontFile.BdfFontFile(fp) + + # Convert to pillow-formatted fonts, which have a .pil and .pbm extension. + p.save(local_bitmap_font_file) + except (SyntaxError, OSError) as err: + raise core.EsphomeError( + f"Failed to parse as bitmap font: '{filepath}': {err}" + ) + + local_pil_font_file = os.path.splitext(local_bitmap_font_file)[0] + ".pil" + return cv.file_(local_pil_font_file) + + +def load_bitmap_font(filepath): from PIL import ImageFont - conf = config[CONF_FILE] - if conf[CONF_TYPE] == TYPE_LOCAL: - path = CORE.relative_config_path(conf[CONF_PATH]) - elif conf[CONF_TYPE] == TYPE_GFONTS: - path = _compute_gfonts_local_path(conf) + # Convert bpf and pcf files to pillow fonts, first. + pil_font_path = convert_bitmap_to_pillow_font(filepath) + try: - font = ImageFont.truetype(str(path), config[CONF_SIZE]) + font = ImageFont.load(str(pil_font_path)) + except Exception as e: + raise core.EsphomeError( + f"Failed to load bitmap font file: {pil_font_path} : {e}" + ) + + return BitmapFontWrapper(font) + + +def load_ttf_font(path, size): + from PIL import ImageFont + + try: + font = ImageFont.truetype(str(path), size) except Exception as e: raise core.EsphomeError(f"Could not load truetype file {path}: {e}") - ascent, descent = font.getmetrics() + return TrueTypeFontWrapper(font) + + +async def to_code(config): + conf = config[CONF_FILE] + if conf[CONF_TYPE] == TYPE_LOCAL_BITMAP: + font = load_bitmap_font(CORE.relative_config_path(conf[CONF_PATH])) + elif conf[CONF_TYPE] == TYPE_LOCAL: + path = CORE.relative_config_path(conf[CONF_PATH]) + font = load_ttf_font(path, config[CONF_SIZE]) + elif conf[CONF_TYPE] == TYPE_GFONTS: + path = _compute_gfonts_local_path(conf) + font = load_ttf_font(path, config[CONF_SIZE]) + else: + raise core.EsphomeError(f"Could not load font: unknown type: {conf[CONF_TYPE]}") + + ascent, descent = font.getmetrics(config[CONF_GLYPHS]) glyph_args = {} data = [] for glyph in config[CONF_GLYPHS]: mask = font.getmask(glyph, mode="1") - _, (offset_x, offset_y) = font.font.getsize(glyph) + offset_x, offset_y = font.getoffset(glyph) width, height = mask.size width8 = ((width + 7) // 8) * 8 glyph_data = [0] * (height * width8 // 8) From 5dec9d88f6e02b6f7d82ac3b531251111d3a5b72 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Mon, 22 Aug 2022 18:26:36 -0700 Subject: [PATCH 22/66] add log messages for bad pronto codes (#3738) Co-authored-by: Samuel Sieb --- esphome/components/remote_base/pronto_protocol.cpp | 1 + .../remote_transmitter/remote_transmitter_esp32.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/esphome/components/remote_base/pronto_protocol.cpp b/esphome/components/remote_base/pronto_protocol.cpp index a2b1a16e07..d434744e49 100644 --- a/esphome/components/remote_base/pronto_protocol.cpp +++ b/esphome/components/remote_base/pronto_protocol.cpp @@ -106,6 +106,7 @@ void ProntoProtocol::send_pronto_(RemoteTransmitData *dst, const std::vectorrmt_temp_.push_back(rmt_item); } + if ((this->rmt_temp_.data() == nullptr) || this->rmt_temp_.empty()) { + ESP_LOGE(TAG, "Empty data"); + return; + } for (uint32_t i = 0; i < send_times; i++) { esp_err_t error = rmt_write_items(this->channel_, this->rmt_temp_.data(), this->rmt_temp_.size(), true); if (error != ESP_OK) { From b854e17995cdb869c0fd166188c756478c622644 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 25 Aug 2022 07:13:44 +1200 Subject: [PATCH 23/66] Initial bluetooth_proxy support (#3736) --- CODEOWNERS | 1 + esphome/components/api/api.proto | 28 ++++ esphome/components/api/api_connection.cpp | 3 + esphome/components/api/api_connection.h | 15 ++ esphome/components/api/api_pb2.cpp | 146 ++++++++++++++++++ esphome/components/api/api_pb2.h | 40 +++++ esphome/components/api/api_pb2_service.cpp | 29 ++++ esphome/components/api/api_pb2_service.h | 7 + esphome/components/api/api_server.cpp | 7 + esphome/components/api/api_server.h | 3 + esphome/components/api/proto.h | 2 +- .../components/bluetooth_proxy/__init__.py | 27 ++++ .../bluetooth_proxy/bluetooth_proxy.cpp | 58 +++++++ .../bluetooth_proxy/bluetooth_proxy.h | 26 ++++ esphome/core/defines.h | 1 + tests/test1.yaml | 2 + 16 files changed, 394 insertions(+), 1 deletion(-) create mode 100644 esphome/components/bluetooth_proxy/__init__.py create mode 100644 esphome/components/bluetooth_proxy/bluetooth_proxy.cpp create mode 100644 esphome/components/bluetooth_proxy/bluetooth_proxy.h diff --git a/CODEOWNERS b/CODEOWNERS index 7b5fa93a3c..53ef69419a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -34,6 +34,7 @@ esphome/components/binary_sensor/* @esphome/core esphome/components/bl0939/* @ziceva esphome/components/bl0940/* @tobias- esphome/components/ble_client/* @buxtronix +esphome/components/bluetooth_proxy/* @jesserockz esphome/components/bme680_bsec/* @trvrnrth esphome/components/bmp3xx/* @martgras esphome/components/button/* @esphome/core diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 88a74540d0..c6cde8b038 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -27,6 +27,7 @@ service APIConnection { rpc subscribe_logs (SubscribeLogsRequest) returns (void) {} rpc subscribe_homeassistant_services (SubscribeHomeassistantServicesRequest) returns (void) {} rpc subscribe_home_assistant_states (SubscribeHomeAssistantStatesRequest) returns (void) {} + rpc subscribe_bluetooth_le_advertisements (SubscribeBluetoothLEAdvertisementsRequest) returns (void) {} rpc get_time (GetTimeRequest) returns (GetTimeResponse) { option (needs_authentication) = false; } @@ -190,6 +191,8 @@ message DeviceInfoResponse { string project_version = 9; uint32 webserver_port = 10; + + bool has_bluetooth_proxy = 11; } message ListEntitiesRequest { @@ -1099,3 +1102,28 @@ message MediaPlayerCommandRequest { bool has_media_url = 6; string media_url = 7; } + +// ==================== BLUETOOTH ==================== +message SubscribeBluetoothLEAdvertisementsRequest { + option (id) = 66; + option (source) = SOURCE_CLIENT; +} + +message BluetoothServiceData { + string uuid = 1; + repeated uint32 data = 2 [packed=false]; +} +message BluetoothLEAdvertisementResponse { + option (id) = 67; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + option (no_delay) = true; + + uint64 address = 1; + string name = 2; + sint32 rssi = 3; + + repeated string service_uuids = 4; + repeated BluetoothServiceData service_data = 5; + repeated BluetoothServiceData manufacturer_data = 6; +} diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 9028034c90..a3b000c778 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -886,6 +886,9 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) { #endif #ifdef USE_WEBSERVER resp.webserver_port = USE_WEBSERVER_PORT; +#endif +#ifdef USE_BLUETOOTH_PROXY + resp.has_bluetooth_proxy = true; #endif return resp; } diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 0787d2f7eb..dcf8bacad2 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -7,6 +7,10 @@ #include "api_server.h" #include "api_frame_helper.h" +#ifdef USE_BLUETOOTH_PROXY +#include "esphome/components/bluetooth_proxy/bluetooth_proxy.h" +#endif + namespace esphome { namespace api { @@ -94,6 +98,13 @@ class APIConnection : public APIServerConnection { return; this->send_homeassistant_service_response(call); } +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call) { + if (!this->bluetooth_le_advertisement_subscription_) + return false; + return this->send_bluetooth_le_advertisement_response(call); + } +#endif #ifdef USE_HOMEASSISTANT_TIME void send_time_request() { GetTimeRequest req; @@ -134,6 +145,9 @@ class APIConnection : public APIServerConnection { return {}; } void execute_service(const ExecuteServiceRequest &msg) override; + void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override { + this->bluetooth_le_advertisement_subscription_ = true; + } bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; } bool is_connection_setup() override { return this->connection_state_ == ConnectionState ::CONNECTED || this->is_authenticated(); @@ -176,6 +190,7 @@ class APIConnection : public APIServerConnection { uint32_t last_traffic_; bool sent_ping_{false}; bool service_call_subscription_{false}; + bool bluetooth_le_advertisement_subscription_{true}; bool next_close_ = false; APIServer *parent_; InitialStateIterator initial_state_iterator_; diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index b91c9bd600..13969fce76 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -495,6 +495,10 @@ bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { this->webserver_port = value.as_uint32(); return true; } + case 11: { + this->has_bluetooth_proxy = value.as_bool(); + return true; + } default: return false; } @@ -544,6 +548,7 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(8, this->project_name); buffer.encode_string(9, this->project_version); buffer.encode_uint32(10, this->webserver_port); + buffer.encode_bool(11, this->has_bluetooth_proxy); } #ifdef HAS_PROTO_MESSAGE_DUMP void DeviceInfoResponse::dump_to(std::string &out) const { @@ -589,6 +594,10 @@ void DeviceInfoResponse::dump_to(std::string &out) const { sprintf(buffer, "%u", this->webserver_port); out.append(buffer); out.append("\n"); + + out.append(" has_bluetooth_proxy: "); + out.append(YESNO(this->has_bluetooth_proxy)); + out.append("\n"); out.append("}"); } #endif @@ -4854,6 +4863,143 @@ void MediaPlayerCommandRequest::dump_to(std::string &out) const { out.append("}"); } #endif +void SubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const {} +#ifdef HAS_PROTO_MESSAGE_DUMP +void SubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const { + out.append("SubscribeBluetoothLEAdvertisementsRequest {}"); +} +#endif +bool BluetoothServiceData::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->data.push_back(value.as_uint32()); + return true; + } + default: + return false; + } +} +bool BluetoothServiceData::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->uuid = value.as_string(); + return true; + } + default: + return false; + } +} +void BluetoothServiceData::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->uuid); + for (auto &it : this->data) { + buffer.encode_uint32(2, it, true); + } +} +#ifdef HAS_PROTO_MESSAGE_DUMP +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->data) { + out.append(" data: "); + sprintf(buffer, "%u", it); + out.append(buffer); + out.append("\n"); + } + out.append("}"); +} +#endif +bool BluetoothLEAdvertisementResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 3: { + this->rssi = value.as_sint32(); + return true; + } + default: + return false; + } +} +bool BluetoothLEAdvertisementResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 2: { + this->name = value.as_string(); + return true; + } + case 4: { + this->service_uuids.push_back(value.as_string()); + return true; + } + case 5: { + this->service_data.push_back(value.as_message()); + return true; + } + case 6: { + this->manufacturer_data.push_back(value.as_message()); + return true; + } + default: + return false; + } +} +void BluetoothLEAdvertisementResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_string(2, this->name); + 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); + } +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothLEAdvertisementResponse {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" rssi: "); + sprintf(buffer, "%d", 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("}"); +} +#endif } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index f9981fdbb7..2093f93ee7 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -263,6 +263,7 @@ class DeviceInfoResponse : public ProtoMessage { std::string project_name{}; std::string project_version{}; uint32_t webserver_port{0}; + bool has_bluetooth_proxy{false}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; @@ -1214,6 +1215,45 @@ class MediaPlayerCommandRequest : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; +class SubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage { + public: + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: +}; +class BluetoothServiceData : public ProtoMessage { + public: + std::string uuid{}; + std::vector data{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class BluetoothLEAdvertisementResponse : public ProtoMessage { + public: + uint64_t address{0}; + std::string name{}; + int32_t rssi{0}; + std::vector service_uuids{}; + std::vector service_data{}; + std::vector manufacturer_data{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index bd146cb54d..6932675c41 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -328,6 +328,14 @@ bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayer #endif #ifdef USE_MEDIA_PLAYER #endif +#ifdef USE_BLUETOOTH_PROXY +bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_bluetooth_le_advertisement_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 67); +} +#endif bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { switch (msg_type) { case 1: { @@ -595,6 +603,15 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, #endif break; } + case 66: { + SubscribeBluetoothLEAdvertisementsRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_subscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str()); +#endif + this->on_subscribe_bluetooth_le_advertisements_request(msg); + break; + } default: return false; } @@ -691,6 +708,18 @@ void APIServerConnection::on_subscribe_home_assistant_states_request(const Subsc } this->subscribe_home_assistant_states(msg); } +void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request( + const SubscribeBluetoothLEAdvertisementsRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->subscribe_bluetooth_le_advertisements(msg); +} void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) { if (!this->is_connection_setup()) { this->on_no_setup_connection(); diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index 28ad3fbd15..49426d1bbc 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -153,6 +153,11 @@ class APIServerConnectionBase : public ProtoService { #endif #ifdef USE_MEDIA_PLAYER virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){}; +#endif + virtual void on_subscribe_bluetooth_le_advertisements_request( + const SubscribeBluetoothLEAdvertisementsRequest &value){}; +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg); #endif protected: bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; @@ -170,6 +175,7 @@ class APIServerConnection : public APIServerConnectionBase { virtual void subscribe_logs(const SubscribeLogsRequest &msg) = 0; virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0; virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0; + virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0; virtual GetTimeResponse get_time(const GetTimeRequest &msg) = 0; virtual void execute_service(const ExecuteServiceRequest &msg) = 0; #ifdef USE_COVER @@ -216,6 +222,7 @@ class APIServerConnection : public APIServerConnectionBase { void on_subscribe_logs_request(const SubscribeLogsRequest &msg) override; void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &msg) override; void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override; + void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override; void on_get_time_request(const GetTimeRequest &msg) override; void on_execute_service_request(const ExecuteServiceRequest &msg) override; #ifdef USE_COVER diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 8375a82313..5851594955 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -291,6 +291,13 @@ void APIServer::send_homeassistant_service_call(const HomeassistantServiceRespon client->send_homeassistant_service_call(call); } } +#ifdef USE_BLUETOOTH_PROXY +void APIServer::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call) { + for (auto &client : this->clients_) { + client->send_bluetooth_le_advertisement(call); + } +} +#endif APIServer::APIServer() { global_api_server = this; } void APIServer::subscribe_home_assistant_state(std::string entity_id, optional attribute, std::function f) { diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 6997e23cac..dc892b2088 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -73,6 +73,9 @@ class APIServer : public Component, public Controller { void on_media_player_update(media_player::MediaPlayer *obj) override; #endif void send_homeassistant_service_call(const HomeassistantServiceResponse &call); +#ifdef USE_BLUETOOTH_PROXY + void send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call); +#endif void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); } #ifdef USE_HOMEASSISTANT_TIME void request_time(); diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 32f525990d..11cd4330ce 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -70,7 +70,7 @@ class ProtoVarInt { } } void encode(std::vector &out) { - uint32_t val = this->value_; + uint64_t val = this->value_; if (val <= 0x7F) { out.push_back(val); return; diff --git a/esphome/components/bluetooth_proxy/__init__.py b/esphome/components/bluetooth_proxy/__init__.py new file mode 100644 index 0000000000..a6a5a4391b --- /dev/null +++ b/esphome/components/bluetooth_proxy/__init__.py @@ -0,0 +1,27 @@ +from esphome.components import esp32_ble_tracker +import esphome.config_validation as cv +import esphome.codegen as cg +from esphome.const import CONF_ID + +DEPENDENCIES = ["esp32", "esp32_ble_tracker"] +CODEOWNERS = ["@jesserockz"] + + +bluetooth_proxy_ns = cg.esphome_ns.namespace("bluetooth_proxy") + +BluetoothProxy = bluetooth_proxy_ns.class_("BluetoothProxy", cg.Component) + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(BluetoothProxy), + } +).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + await esp32_ble_tracker.register_ble_device(var, config) + + cg.add_define("USE_BLUETOOTH_PROXY") diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp new file mode 100644 index 0000000000..41871295ce --- /dev/null +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -0,0 +1,58 @@ +#include "bluetooth_proxy.h" + +#ifdef USE_API +#include "esphome/components/api/api_pb2.h" +#include "esphome/components/api/api_server.h" +#endif // USE_API +#include "esphome/core/log.h" + +#ifdef USE_ESP32 + +namespace esphome { +namespace bluetooth_proxy { + +static const char *const TAG = "bluetooth_proxy"; + +bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { + ESP_LOGV(TAG, "Proxying packet from %s - %s. RSSI: %d dB", device.get_name().c_str(), device.address_str().c_str(), + device.get_rssi()); + this->send_api_packet_(device); + return true; +} + +void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) { +#ifndef USE_API + return; +#else + api::BluetoothLEAdvertisementResponse resp; + resp.address = device.address_uint64(); + if (!device.get_name().empty()) + resp.name = device.get_name(); + resp.rssi = device.get_rssi(); + for (auto uuid : device.get_service_uuids()) { + resp.service_uuids.push_back(uuid.to_string()); + } + for (auto &data : device.get_service_datas()) { + api::BluetoothServiceData service_data; + service_data.uuid = data.uuid.to_string(); + for (auto d : data.data) + service_data.data.push_back(d); + resp.service_data.push_back(service_data); + } + for (auto &data : device.get_manufacturer_datas()) { + api::BluetoothServiceData manufacturer_data; + manufacturer_data.uuid = data.uuid.to_string(); + for (auto d : data.data) + manufacturer_data.data.push_back(d); + resp.manufacturer_data.push_back(manufacturer_data); + } + api::global_api_server->send_bluetooth_le_advertisement(resp); +#endif +} + +void BluetoothProxy::dump_config() { ESP_LOGCONFIG(TAG, "Bluetooth Proxy:"); } + +} // namespace bluetooth_proxy +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h new file mode 100644 index 0000000000..9a936747c0 --- /dev/null +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -0,0 +1,26 @@ +#pragma once + +#ifdef USE_ESP32 + +#include + +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" +#include "esphome/core/automation.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace bluetooth_proxy { + +class BluetoothProxy : public Component, public esp32_ble_tracker::ESPBTDeviceListener { + public: + bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; + void dump_config() override; + + protected: + void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device); +}; + +} // namespace bluetooth_proxy +} // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 727b35e6f9..90676c421e 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -71,6 +71,7 @@ #define USE_IMPROV #define USE_SOCKET_IMPL_BSD_SOCKETS #define USE_WIFI_11KV_SUPPORT +#define USE_BLUETOOTH_PROXY #ifdef USE_ARDUINO #define USE_ARDUINO_VERSION_CODE VERSION_CODE(1, 0, 6) diff --git a/tests/test1.yaml b/tests/test1.yaml index 5bd406a10f..fcc7107bd6 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -288,6 +288,8 @@ adalight: esp32_ble_tracker: +bluetooth_proxy: + ble_client: - mac_address: AA:BB:CC:DD:EE:FF id: ble_foo From 63290a265ccfe5c75c80842bc2f91e4915f541a1 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Thu, 25 Aug 2022 03:41:52 +0200 Subject: [PATCH 24/66] add color compare operator's (#3730) --- esphome/core/color.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/esphome/core/color.h b/esphome/core/color.h index 7596eeb0cf..7062a2a8c8 100644 --- a/esphome/core/color.h +++ b/esphome/core/color.h @@ -44,6 +44,20 @@ struct Color { w((colorcode >> 24) & 0xFF) {} inline bool is_on() ALWAYS_INLINE { return this->raw_32 != 0; } + + inline bool operator==(const Color &rhs) { // NOLINT + return this->raw_32 == rhs.raw_32; + } + inline bool operator==(uint32_t colorcode) { // NOLINT + return this->raw_32 == colorcode; + } + inline bool operator!=(const Color &rhs) { // NOLINT + return this->raw_32 != rhs.raw_32; + } + inline bool operator!=(uint32_t colorcode) { // NOLINT + return this->raw_32 != colorcode; + } + inline Color &operator=(const Color &rhs) ALWAYS_INLINE { // NOLINT this->r = rhs.r; this->g = rhs.g; From 8fa18ca7c7741d640d9e1c0fa33abb434593d4c1 Mon Sep 17 00:00:00 2001 From: MrEditor97 Date: Thu, 25 Aug 2022 04:49:31 +0100 Subject: [PATCH 25/66] Support for MCP9600 Thermocouple Amplifier (#3700) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/mcp9600/__init__.py | 0 esphome/components/mcp9600/mcp9600.cpp | 115 +++++++++++++++++++++++++ esphome/components/mcp9600/mcp9600.h | 51 +++++++++++ esphome/components/mcp9600/sensor.py | 81 +++++++++++++++++ tests/test5.yaml | 8 ++ 6 files changed, 256 insertions(+) create mode 100644 esphome/components/mcp9600/__init__.py create mode 100644 esphome/components/mcp9600/mcp9600.cpp create mode 100644 esphome/components/mcp9600/mcp9600.h create mode 100644 esphome/components/mcp9600/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index 53ef69419a..fe6e712d45 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -122,6 +122,7 @@ esphome/components/mcp2515/* @danielschramm @mvturnho esphome/components/mcp3204/* @rsumner esphome/components/mcp4728/* @berfenger esphome/components/mcp47a1/* @jesserockz +esphome/components/mcp9600/* @MrEditor97 esphome/components/mcp9808/* @k7hpn esphome/components/md5/* @esphome/core esphome/components/mdns/* @esphome/core diff --git a/esphome/components/mcp9600/__init__.py b/esphome/components/mcp9600/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/mcp9600/mcp9600.cpp b/esphome/components/mcp9600/mcp9600.cpp new file mode 100644 index 0000000000..3fdd788fc6 --- /dev/null +++ b/esphome/components/mcp9600/mcp9600.cpp @@ -0,0 +1,115 @@ +#include "mcp9600.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace mcp9600 { + +static const char *const TAG = "mcp9600"; + +static const uint8_t MCP9600_REGISTER_HOT_JUNCTION = 0x00; +// static const uint8_t MCP9600_REGISTER_JUNCTION_DELTA = 0x01; // Unused, but kept for future reference +static const uint8_t MCP9600_REGISTER_COLD_JUNTION = 0x02; +// static const uint8_t MCP9600_REGISTER_RAW_DATA_ADC = 0x03; // Unused, but kept for future reference +static const uint8_t MCP9600_REGISTER_STATUS = 0x04; +static const uint8_t MCP9600_REGISTER_SENSOR_CONFIG = 0x05; +static const uint8_t MCP9600_REGISTER_CONFIG = 0x06; +static const uint8_t MCP9600_REGISTER_ALERT1_CONFIG = 0x08; +static const uint8_t MCP9600_REGISTER_ALERT2_CONFIG = 0x09; +static const uint8_t MCP9600_REGISTER_ALERT3_CONFIG = 0x0A; +static const uint8_t MCP9600_REGISTER_ALERT4_CONFIG = 0x0B; +static const uint8_t MCP9600_REGISTER_ALERT1_HYSTERESIS = 0x0C; +static const uint8_t MCP9600_REGISTER_ALERT2_HYSTERESIS = 0x0D; +static const uint8_t MCP9600_REGISTER_ALERT3_HYSTERESIS = 0x0E; +static const uint8_t MCP9600_REGISTER_ALERT4_HYSTERESIS = 0x0F; +static const uint8_t MCP9600_REGISTER_ALERT1_LIMIT = 0x10; +static const uint8_t MCP9600_REGISTER_ALERT2_LIMIT = 0x11; +static const uint8_t MCP9600_REGISTER_ALERT3_LIMIT = 0x12; +static const uint8_t MCP9600_REGISTER_ALERT4_LIMIT = 0x13; +static const uint8_t MCP9600_REGISTER_DEVICE_ID = 0x20; + +void MCP9600Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up MCP9600..."); + + uint16_t dev_id = 0; + this->read_byte_16(MCP9600_REGISTER_DEVICE_ID, &dev_id); + this->device_id_ = (uint8_t)(dev_id >> 8); + + // Allows both MCP9600's and MCP9601's to be connected. + if (this->device_id_ != (uint8_t) 0x40 && this->device_id_ != (uint8_t) 0x41) { + this->error_code_ = COMMUNICATION_FAILED; + this->mark_failed(); + return; + } + + bool success = this->write_byte(MCP9600_REGISTER_STATUS, 0x00); + success |= this->write_byte(MCP9600_REGISTER_SENSOR_CONFIG, uint8_t(0x00 | thermocouple_type_ << 4)); + success |= this->write_byte(MCP9600_REGISTER_CONFIG, 0x00); + success |= this->write_byte(MCP9600_REGISTER_ALERT1_CONFIG, 0x00); + success |= this->write_byte(MCP9600_REGISTER_ALERT2_CONFIG, 0x00); + success |= this->write_byte(MCP9600_REGISTER_ALERT3_CONFIG, 0x00); + success |= this->write_byte(MCP9600_REGISTER_ALERT4_CONFIG, 0x00); + success |= this->write_byte(MCP9600_REGISTER_ALERT1_HYSTERESIS, 0x00); + success |= this->write_byte(MCP9600_REGISTER_ALERT2_HYSTERESIS, 0x00); + success |= this->write_byte(MCP9600_REGISTER_ALERT3_HYSTERESIS, 0x00); + success |= this->write_byte(MCP9600_REGISTER_ALERT4_HYSTERESIS, 0x00); + success |= this->write_byte_16(MCP9600_REGISTER_ALERT1_LIMIT, 0x0000); + success |= this->write_byte_16(MCP9600_REGISTER_ALERT2_LIMIT, 0x0000); + success |= this->write_byte_16(MCP9600_REGISTER_ALERT3_LIMIT, 0x0000); + success |= this->write_byte_16(MCP9600_REGISTER_ALERT4_LIMIT, 0x0000); + + if (!success) { + this->error_code_ = FAILED_TO_UPDATE_CONFIGURATION; + this->mark_failed(); + return; + } +} + +void MCP9600Component::dump_config() { + ESP_LOGCONFIG(TAG, "MCP9600:"); + LOG_I2C_DEVICE(this); + LOG_UPDATE_INTERVAL(this); + + ESP_LOGCONFIG(TAG, " Device ID: 0x%x", this->device_id_); + + LOG_SENSOR(" ", "Hot Junction Temperature", this->hot_junction_sensor_); + LOG_SENSOR(" ", "Cold Junction Temperature", this->cold_junction_sensor_); + + switch (this->error_code_) { + case COMMUNICATION_FAILED: + ESP_LOGE(TAG, "Connected device does not match a known MCP9600 or MCP901 sensor"); + break; + case FAILED_TO_UPDATE_CONFIGURATION: + ESP_LOGE(TAG, "Failed to update device configuration"); + break; + case NONE: + default: + break; + } +} + +void MCP9600Component::update() { + if (this->hot_junction_sensor_ != nullptr) { + uint16_t raw_hot_junction_temperature; + if (!this->read_byte_16(MCP9600_REGISTER_HOT_JUNCTION, &raw_hot_junction_temperature)) { + this->status_set_warning(); + return; + } + float hot_junction_temperature = int16_t(raw_hot_junction_temperature) * 0.0625; + this->hot_junction_sensor_->publish_state(hot_junction_temperature); + } + + if (this->cold_junction_sensor_ != nullptr) { + uint16_t raw_cold_junction_temperature; + if (!this->read_byte_16(MCP9600_REGISTER_COLD_JUNTION, &raw_cold_junction_temperature)) { + this->status_set_warning(); + return; + } + float cold_junction_temperature = int16_t(raw_cold_junction_temperature) * 0.0625; + this->cold_junction_sensor_->publish_state(cold_junction_temperature); + } + + this->status_clear_warning(); +} + +} // namespace mcp9600 +} // namespace esphome diff --git a/esphome/components/mcp9600/mcp9600.h b/esphome/components/mcp9600/mcp9600.h new file mode 100644 index 0000000000..92612cc26d --- /dev/null +++ b/esphome/components/mcp9600/mcp9600.h @@ -0,0 +1,51 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace mcp9600 { + +enum MCP9600ThermocoupleType : uint8_t { + MCP9600_THERMOCOUPLE_TYPE_K = 0b000, + MCP9600_THERMOCOUPLE_TYPE_J = 0b001, + MCP9600_THERMOCOUPLE_TYPE_T = 0b010, + MCP9600_THERMOCOUPLE_TYPE_N = 0b011, + MCP9600_THERMOCOUPLE_TYPE_S = 0b100, + MCP9600_THERMOCOUPLE_TYPE_E = 0b101, + MCP9600_THERMOCOUPLE_TYPE_B = 0b110, + MCP9600_THERMOCOUPLE_TYPE_R = 0b111, +}; + +class MCP9600Component : public PollingComponent, public i2c::I2CDevice { + public: + void setup() override; + void dump_config() override; + void update() override; + + float get_setup_priority() const override { return setup_priority::DATA; } + + void set_hot_junction(sensor::Sensor *hot_junction) { this->hot_junction_sensor_ = hot_junction; } + void set_cold_junction(sensor::Sensor *cold_junction) { this->cold_junction_sensor_ = cold_junction; } + void set_thermocouple_type(MCP9600ThermocoupleType thermocouple_type) { + this->thermocouple_type_ = thermocouple_type; + }; + + protected: + uint8_t device_id_{0}; + + sensor::Sensor *hot_junction_sensor_{nullptr}; + sensor::Sensor *cold_junction_sensor_{nullptr}; + + MCP9600ThermocoupleType thermocouple_type_{MCP9600_THERMOCOUPLE_TYPE_K}; + + enum ErrorCode { + NONE, + COMMUNICATION_FAILED, + FAILED_TO_UPDATE_CONFIGURATION, + } error_code_{NONE}; +}; + +} // namespace mcp9600 +} // namespace esphome diff --git a/esphome/components/mcp9600/sensor.py b/esphome/components/mcp9600/sensor.py new file mode 100644 index 0000000000..4c10df2dab --- /dev/null +++ b/esphome/components/mcp9600/sensor.py @@ -0,0 +1,81 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, sensor +from esphome.const import ( + CONF_ID, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, +) + +CONF_THERMOCOUPLE_TYPE = "thermocouple_type" +CONF_HOT_JUNCTION = "hot_junction" +CONF_COLD_JUNCTION = "cold_junction" + +DEPENDENCIES = ["i2c"] +CODEOWNERS = ["@MrEditor97"] + +mcp9600_ns = cg.esphome_ns.namespace("mcp9600") +MCP9600Component = mcp9600_ns.class_( + "MCP9600Component", cg.PollingComponent, i2c.I2CDevice +) + +MCP9600ThermocoupleType = mcp9600_ns.enum("MCP9600ThermocoupleType") +THERMOCOUPLE_TYPE = { + "K": MCP9600ThermocoupleType.MCP9600_THERMOCOUPLE_TYPE_K, + "J": MCP9600ThermocoupleType.MCP9600_THERMOCOUPLE_TYPE_J, + "T": MCP9600ThermocoupleType.MCP9600_THERMOCOUPLE_TYPE_T, + "N": MCP9600ThermocoupleType.MCP9600_THERMOCOUPLE_TYPE_N, + "S": MCP9600ThermocoupleType.MCP9600_THERMOCOUPLE_TYPE_S, + "E": MCP9600ThermocoupleType.MCP9600_THERMOCOUPLE_TYPE_E, + "B": MCP9600ThermocoupleType.MCP9600_THERMOCOUPLE_TYPE_B, + "R": MCP9600ThermocoupleType.MCP9600_THERMOCOUPLE_TYPE_R, +} + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MCP9600Component), + cv.Optional(CONF_THERMOCOUPLE_TYPE, default="K"): cv.enum( + THERMOCOUPLE_TYPE, upper=True + ), + cv.Optional(CONF_HOT_JUNCTION): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_COLD_JUNCTION): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x67)) +) + +FINAL_VALIDATE_SCHEMA = i2c.final_validate_device_schema( + "mcp9600", min_frequency="100khz" +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + + cg.add(var.set_thermocouple_type(config[CONF_THERMOCOUPLE_TYPE])) + + if CONF_HOT_JUNCTION in config: + conf = config[CONF_HOT_JUNCTION] + sens = await sensor.new_sensor(conf) + cg.add(var.set_hot_junction(sens)) + + if CONF_COLD_JUNCTION in config: + conf = config[CONF_COLD_JUNCTION] + sens = await sensor.new_sensor(conf) + cg.add(var.set_cold_junction(sens)) diff --git a/tests/test5.yaml b/tests/test5.yaml index 35f6b14f2a..c193009ead 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -38,6 +38,7 @@ uart: baud_rate: 19200 i2c: + frequency: 100khz modbus: uart_id: uart1 @@ -328,6 +329,13 @@ sensor: store_baseline: true address: 0x69 + - platform: mcp9600 + thermocouple_type: K + hot_junction: + name: "Thermocouple Temperature" + cold_junction: + name: "Ambient Temperature" + script: - id: automation_test then: From 281916653933fb5c457ed7201c195299ea8cc03b Mon Sep 17 00:00:00 2001 From: Frank Riley Date: Wed, 24 Aug 2022 21:12:45 -0700 Subject: [PATCH 26/66] Support high update rates and fix several bugs in the cse7766 component. (#3675) --- esphome/components/cse7766/cse7766.cpp | 135 +++++++++++++------------ esphome/components/cse7766/cse7766.h | 2 + 2 files changed, 75 insertions(+), 62 deletions(-) diff --git a/esphome/components/cse7766/cse7766.cpp b/esphome/components/cse7766/cse7766.cpp index 25d75da3e6..f232f35ea6 100644 --- a/esphome/components/cse7766/cse7766.cpp +++ b/esphome/components/cse7766/cse7766.cpp @@ -13,8 +13,9 @@ void CSE7766Component::loop() { this->raw_data_index_ = 0; } - if (this->available() == 0) + if (this->available() == 0) { return; + } this->last_transmission_ = now; while (this->available() != 0) { @@ -22,6 +23,7 @@ void CSE7766Component::loop() { if (!this->check_byte_()) { this->raw_data_index_ = 0; this->status_set_warning(); + continue; } if (this->raw_data_index_ == 23) { @@ -51,8 +53,9 @@ bool CSE7766Component::check_byte_() { if (index == 23) { uint8_t checksum = 0; - for (uint8_t i = 2; i < 23; i++) + for (uint8_t i = 2; i < 23; i++) { checksum += this->raw_data_[i]; + } if (checksum != this->raw_data_[23]) { ESP_LOGW(TAG, "Invalid checksum from CSE7766: 0x%02X != 0x%02X", checksum, this->raw_data_[23]); @@ -66,20 +69,34 @@ bool CSE7766Component::check_byte_() { void CSE7766Component::parse_data_() { ESP_LOGVV(TAG, "CSE7766 Data: "); for (uint8_t i = 0; i < 23; i++) { - ESP_LOGVV(TAG, " i=%u: 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", i, BYTE_TO_BINARY(this->raw_data_[i]), + ESP_LOGVV(TAG, " %u: 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", i + 1, BYTE_TO_BINARY(this->raw_data_[i]), this->raw_data_[i]); } uint8_t header1 = this->raw_data_[0]; if (header1 == 0xAA) { - ESP_LOGW(TAG, "CSE7766 not calibrated!"); + ESP_LOGE(TAG, "CSE7766 not calibrated!"); return; } - if ((header1 & 0xF0) == 0xF0 && ((header1 >> 0) & 1) == 1) { - ESP_LOGW(TAG, "CSE7766 reports abnormal hardware: (0x%02X)", header1); - ESP_LOGW(TAG, " Coefficient storage area is abnormal."); - return; + bool power_cycle_exceeds_range = false; + + if ((header1 & 0xF0) == 0xF0) { + if (header1 & 0xD) { + ESP_LOGE(TAG, "CSE7766 reports abnormal external circuit or chip damage: (0x%02X)", header1); + if (header1 & (1 << 3)) { + ESP_LOGE(TAG, " Voltage cycle exceeds range."); + } + if (header1 & (1 << 2)) { + ESP_LOGE(TAG, " Current cycle exceeds range."); + } + if (header1 & (1 << 0)) { + ESP_LOGE(TAG, " Coefficient storage area is abnormal."); + } + return; + } + + power_cycle_exceeds_range = header1 & (1 << 1); } uint32_t voltage_calib = this->get_24_bit_uint_(2); @@ -92,46 +109,29 @@ void CSE7766Component::parse_data_() { uint8_t adj = this->raw_data_[20]; uint32_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22]; - bool power_ok = true; - bool voltage_ok = true; - bool current_ok = true; - - if (header1 > 0xF0) { - // ESP_LOGV(TAG, "CSE7766 reports abnormal hardware: (0x%02X)", byte); - if ((header1 >> 3) & 1) { - ESP_LOGV(TAG, " Voltage cycle exceeds range."); - voltage_ok = false; - } - if ((header1 >> 2) & 1) { - ESP_LOGV(TAG, " Current cycle exceeds range."); - current_ok = false; - } - if ((header1 >> 1) & 1) { - ESP_LOGV(TAG, " Power cycle exceeds range."); - power_ok = false; - } - if ((header1 >> 0) & 1) { - ESP_LOGV(TAG, " Coefficient storage area is abnormal."); - return; - } - } - - if ((adj & 0x40) == 0x40 && voltage_ok && current_ok) { + bool have_voltage = adj & 0x40; + if (have_voltage) { // voltage cycle of serial port outputted is a complete cycle; this->voltage_acc_ += voltage_calib / float(voltage_cycle); this->voltage_counts_ += 1; } - float power = 0; - if ((adj & 0x10) == 0x10 && voltage_ok && current_ok && power_ok) { + bool have_power = adj & 0x10; + float power = 0.0f; + + if (have_power) { // power cycle of serial port outputted is a complete cycle; - power = power_calib / float(power_cycle); + // According to the user manual, power cycle exceeding range means the measured power is 0 + if (!power_cycle_exceeds_range) { + power = power_calib / float(power_cycle); + } this->power_acc_ += power; this->power_counts_ += 1; uint32_t difference; - if (this->cf_pulses_last_ == 0) + if (this->cf_pulses_last_ == 0) { this->cf_pulses_last_ = cf_pulses; + } if (cf_pulses < this->cf_pulses_last_) { difference = cf_pulses + (0x10000 - this->cf_pulses_last_); @@ -139,41 +139,52 @@ void CSE7766Component::parse_data_() { difference = cf_pulses - this->cf_pulses_last_; } this->cf_pulses_last_ = cf_pulses; - this->energy_total_ += difference * float(power_calib) / 1000000.0 / 3600.0; + this->energy_total_ += difference * float(power_calib) / 1000000.0f / 3600.0f; + this->energy_total_counts_ += 1; } - if ((adj & 0x20) == 0x20 && current_ok && voltage_ok && power != 0.0) { + if (adj & 0x20) { // indicates current cycle of serial port outputted is a complete cycle; - this->current_acc_ += current_calib / float(current_cycle); + float current = 0.0f; + if (have_voltage && !have_power) { + // Testing has shown that when we have voltage and current but not power, that means the power is 0. + // We report a power of 0, which in turn means we should report a current of 0. + this->power_counts_ += 1; + } else if (power != 0.0f) { + current = current_calib / float(current_cycle); + } + this->current_acc_ += current; this->current_counts_ += 1; } } void CSE7766Component::update() { - float voltage = this->voltage_counts_ > 0 ? this->voltage_acc_ / this->voltage_counts_ : 0.0f; - float current = this->current_counts_ > 0 ? this->current_acc_ / this->current_counts_ : 0.0f; - float power = this->power_counts_ > 0 ? this->power_acc_ / this->power_counts_ : 0.0f; + const auto publish_state = [](const char *name, sensor::Sensor *sensor, float &acc, uint32_t &counts) { + if (counts != 0) { + const auto avg = acc / counts; - ESP_LOGV(TAG, "Got voltage_acc=%.2f current_acc=%.2f power_acc=%.2f", this->voltage_acc_, this->current_acc_, - this->power_acc_); - ESP_LOGV(TAG, "Got voltage_counts=%d current_counts=%d power_counts=%d", this->voltage_counts_, this->current_counts_, - this->power_counts_); - ESP_LOGD(TAG, "Got voltage=%.1fV current=%.1fA power=%.1fW", voltage, current, power); + ESP_LOGV(TAG, "Got %s_acc=%.2f %s_counts=%d %s=%.1f", name, acc, name, counts, name, avg); - if (this->voltage_sensor_ != nullptr) - this->voltage_sensor_->publish_state(voltage); - if (this->current_sensor_ != nullptr) - this->current_sensor_->publish_state(current); - if (this->power_sensor_ != nullptr) - this->power_sensor_->publish_state(power); - if (this->energy_sensor_ != nullptr) - this->energy_sensor_->publish_state(this->energy_total_); + if (sensor != nullptr) { + sensor->publish_state(avg); + } - this->voltage_acc_ = 0.0f; - this->current_acc_ = 0.0f; - this->power_acc_ = 0.0f; - this->voltage_counts_ = 0; - this->power_counts_ = 0; - this->current_counts_ = 0; + acc = 0.0f; + counts = 0; + } + }; + + publish_state("voltage", this->voltage_sensor_, this->voltage_acc_, this->voltage_counts_); + publish_state("current", this->current_sensor_, this->current_acc_, this->current_counts_); + publish_state("power", this->power_sensor_, this->power_acc_, this->power_counts_); + + if (this->energy_total_counts_ != 0) { + ESP_LOGV(TAG, "Got energy_total=%.2f energy_total_counts=%d", this->energy_total_, this->energy_total_counts_); + + if (this->energy_sensor_ != nullptr) { + this->energy_sensor_->publish_state(this->energy_total_); + } + this->energy_total_counts_ = 0; + } } uint32_t CSE7766Component::get_24_bit_uint_(uint8_t start_index) { diff --git a/esphome/components/cse7766/cse7766.h b/esphome/components/cse7766/cse7766.h index d6062c251c..2f30eec09f 100644 --- a/esphome/components/cse7766/cse7766.h +++ b/esphome/components/cse7766/cse7766.h @@ -39,6 +39,8 @@ class CSE7766Component : public PollingComponent, public uart::UARTDevice { uint32_t voltage_counts_{0}; uint32_t current_counts_{0}; uint32_t power_counts_{0}; + // Setting this to 1 means it will always publish 0 once at startup + uint32_t energy_total_counts_{1}; }; } // namespace cse7766 From 84bac8356a76ee1710629f7c0dca81f201cf2617 Mon Sep 17 00:00:00 2001 From: Jan Grewe Date: Tue, 30 Aug 2022 00:55:55 +0200 Subject: [PATCH 27/66] Add Prometheus metrics relabeling (#3734) Co-authored-by: Guillermo Ruffino --- esphome/components/prometheus/__init__.py | 23 ++++ .../prometheus/prometheus_handler.cpp | 114 ++++++++++-------- .../prometheus/prometheus_handler.h | 22 ++++ esphome/config.py | 2 + esphome/const.py | 1 + tests/test4.yaml | 7 ++ 6 files changed, 117 insertions(+), 52 deletions(-) diff --git a/esphome/components/prometheus/__init__.py b/esphome/components/prometheus/__init__.py index e7c0459251..5b63710c6a 100644 --- a/esphome/components/prometheus/__init__.py +++ b/esphome/components/prometheus/__init__.py @@ -2,16 +2,27 @@ import esphome.config_validation as cv import esphome.codegen as cg from esphome.const import ( CONF_ID, + CONF_NAME, CONF_INCLUDE_INTERNAL, + CONF_RELABEL, ) from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID from esphome.components import web_server_base +from esphome.cpp_types import EntityBase AUTO_LOAD = ["web_server_base"] prometheus_ns = cg.esphome_ns.namespace("prometheus") PrometheusHandler = prometheus_ns.class_("PrometheusHandler", cg.Component) +CUSTOMIZED_ENTITY = cv.Schema( + { + cv.Optional(CONF_ID): cv.string_strict, + cv.Optional(CONF_NAME): cv.string_strict, + }, + cv.has_at_least_one_key, +) + CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(PrometheusHandler), @@ -19,6 +30,11 @@ CONFIG_SCHEMA = cv.Schema( web_server_base.WebServerBase ), cv.Optional(CONF_INCLUDE_INTERNAL, default=False): cv.boolean, + cv.Optional(CONF_RELABEL, default={}): cv.Schema( + { + cv.use_id(EntityBase): CUSTOMIZED_ENTITY, + } + ), }, cv.only_with_arduino, ).extend(cv.COMPONENT_SCHEMA) @@ -33,3 +49,10 @@ async def to_code(config): await cg.register_component(var, config) cg.add(var.set_include_internal(config[CONF_INCLUDE_INTERNAL])) + + for key, value in config[CONF_RELABEL].items(): + entity = await cg.get_variable(key) + if CONF_ID in value: + cg.add(var.add_label_id(entity, value[CONF_ID])) + if CONF_NAME in value: + cg.add(var.add_label_name(entity, value[CONF_NAME])) diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index a52347ba57..abb5111aaf 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -54,6 +54,16 @@ void PrometheusHandler::handleRequest(AsyncWebServerRequest *req) { req->send(stream); } +std::string PrometheusHandler::relabel_id_(EntityBase *obj) { + auto item = relabel_map_id_.find(obj); + return item == relabel_map_id_.end() ? obj->get_object_id() : item->second; +} + +std::string PrometheusHandler::relabel_name_(EntityBase *obj) { + auto item = relabel_map_name_.find(obj); + return item == relabel_map_name_.end() ? obj->get_name() : item->second; +} + // Type-specific implementation #ifdef USE_SENSOR void PrometheusHandler::sensor_type_(AsyncResponseStream *stream) { @@ -66,15 +76,15 @@ void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor if (!std::isnan(obj->state)) { // We have a valid value, output this value stream->print(F("esphome_sensor_failed{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 0\n")); // Data itself stream->print(F("esphome_sensor_value{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\",unit=\"")); stream->print(obj->get_unit_of_measurement().c_str()); stream->print(F("\"} ")); @@ -83,9 +93,9 @@ void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor } else { // Invalid state stream->print(F("esphome_sensor_failed{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 1\n")); } } @@ -103,24 +113,24 @@ void PrometheusHandler::binary_sensor_row_(AsyncResponseStream *stream, binary_s if (obj->has_state()) { // We have a valid value, output this value stream->print(F("esphome_binary_sensor_failed{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 0\n")); // Data itself stream->print(F("esphome_binary_sensor_value{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->state); stream->print('\n'); } else { // Invalid state stream->print(F("esphome_binary_sensor_failed{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 1\n")); } } @@ -137,24 +147,24 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) { if (obj->is_internal() && !this->include_internal_) return; stream->print(F("esphome_fan_failed{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 0\n")); // Data itself stream->print(F("esphome_fan_value{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->state); stream->print('\n'); // Speed if available if (obj->get_traits().supports_speed()) { stream->print(F("esphome_fan_speed{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->speed); stream->print('\n'); @@ -162,9 +172,9 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) { // Oscillation if available if (obj->get_traits().supports_oscillation()) { stream->print(F("esphome_fan_oscillation{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->oscillating); stream->print('\n'); @@ -183,9 +193,9 @@ void PrometheusHandler::light_row_(AsyncResponseStream *stream, light::LightStat return; // State stream->print(F("esphome_light_state{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->remote_values.is_on()); stream->print(F("\n")); @@ -195,37 +205,37 @@ void PrometheusHandler::light_row_(AsyncResponseStream *stream, light::LightStat color.as_brightness(&brightness); color.as_rgbw(&r, &g, &b, &w); stream->print(F("esphome_light_color{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\",channel=\"brightness\"} ")); stream->print(brightness); stream->print(F("\n")); stream->print(F("esphome_light_color{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\",channel=\"r\"} ")); stream->print(r); stream->print(F("\n")); stream->print(F("esphome_light_color{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\",channel=\"g\"} ")); stream->print(g); stream->print(F("\n")); stream->print(F("esphome_light_color{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\",channel=\"b\"} ")); stream->print(b); stream->print(F("\n")); stream->print(F("esphome_light_color{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\",channel=\"w\"} ")); stream->print(w); stream->print(F("\n")); @@ -233,15 +243,15 @@ void PrometheusHandler::light_row_(AsyncResponseStream *stream, light::LightStat std::string effect = obj->get_effect_name(); if (effect == "None") { stream->print(F("esphome_light_effect_active{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\",effect=\"None\"} 0\n")); } else { stream->print(F("esphome_light_effect_active{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\",effect=\"")); stream->print(effect.c_str()); stream->print(F("\"} 1\n")); @@ -260,23 +270,23 @@ void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *ob if (!std::isnan(obj->position)) { // We have a valid value, output this value stream->print(F("esphome_cover_failed{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 0\n")); // Data itself stream->print(F("esphome_cover_value{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->position); stream->print('\n'); if (obj->get_traits().get_supports_tilt()) { stream->print(F("esphome_cover_tilt{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->tilt); stream->print('\n'); @@ -284,9 +294,9 @@ void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *ob } else { // Invalid state stream->print(F("esphome_cover_failed{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 1\n")); } } @@ -301,15 +311,15 @@ void PrometheusHandler::switch_row_(AsyncResponseStream *stream, switch_::Switch if (obj->is_internal() && !this->include_internal_) return; stream->print(F("esphome_switch_failed{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 0\n")); // Data itself stream->print(F("esphome_switch_value{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->state); stream->print('\n'); @@ -325,15 +335,15 @@ void PrometheusHandler::lock_row_(AsyncResponseStream *stream, lock::Lock *obj) if (obj->is_internal() && !this->include_internal_) return; stream->print(F("esphome_lock_failed{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 0\n")); // Data itself stream->print(F("esphome_lock_value{id=\"")); - stream->print(obj->get_object_id().c_str()); + stream->print(relabel_id_(obj).c_str()); stream->print(F("\",name=\"")); - stream->print(obj->get_name().c_str()); + stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); stream->print(obj->state); stream->print('\n'); diff --git a/esphome/components/prometheus/prometheus_handler.h b/esphome/components/prometheus/prometheus_handler.h index b378e46ea3..f416ecf246 100644 --- a/esphome/components/prometheus/prometheus_handler.h +++ b/esphome/components/prometheus/prometheus_handler.h @@ -2,6 +2,9 @@ #ifdef USE_ARDUINO +#include +#include + #include "esphome/components/web_server_base/web_server_base.h" #include "esphome/core/controller.h" #include "esphome/core/component.h" @@ -20,6 +23,20 @@ class PrometheusHandler : public AsyncWebHandler, public Component { */ void set_include_internal(bool include_internal) { include_internal_ = include_internal; } + /** Add the value for an entity's "id" label. + * + * @param obj The entity for which to set the "id" label + * @param value The value for the "id" label + */ + void add_label_id(EntityBase *obj, const std::string &value) { relabel_map_id_.insert({obj, value}); } + + /** Add the value for an entity's "name" label. + * + * @param obj The entity for which to set the "name" label + * @param value The value for the "name" label + */ + void add_label_name(EntityBase *obj, const std::string &value) { relabel_map_name_.insert({obj, value}); } + bool canHandle(AsyncWebServerRequest *request) override { if (request->method() == HTTP_GET) { if (request->url() == "/metrics") @@ -41,6 +58,9 @@ class PrometheusHandler : public AsyncWebHandler, public Component { } protected: + std::string relabel_id_(EntityBase *obj); + std::string relabel_name_(EntityBase *obj); + #ifdef USE_SENSOR /// Return the type for prometheus void sensor_type_(AsyncResponseStream *stream); @@ -92,6 +112,8 @@ class PrometheusHandler : public AsyncWebHandler, public Component { web_server_base::WebServerBase *base_; bool include_internal_{false}; + std::map relabel_map_id_; + std::map relabel_map_name_; }; } // namespace prometheus diff --git a/esphome/config.py b/esphome/config.py index 545b805367..56fe75a4c7 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -244,6 +244,8 @@ def iter_ids(config, path=None): yield from iter_ids(item, path + [i]) elif isinstance(config, dict): for key, value in config.items(): + if isinstance(key, core.ID): + yield key, path yield from iter_ids(value, path + [key]) diff --git a/esphome/const.py b/esphome/const.py index 7735e96661..5d3ddcf518 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -565,6 +565,7 @@ CONF_REF = "ref" CONF_REFERENCE_RESISTANCE = "reference_resistance" CONF_REFERENCE_TEMPERATURE = "reference_temperature" CONF_REFRESH = "refresh" +CONF_RELABEL = "relabel" CONF_REPEAT = "repeat" CONF_REPOSITORY = "repository" CONF_RESET_DURATION = "reset_duration" diff --git a/tests/test4.yaml b/tests/test4.yaml index 847639289e..c3079037c1 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -667,3 +667,10 @@ media_player: - media_player.volume_up: - media_player.volume_down: - media_player.volume_set: 50% + +prometheus: + include_internal: true + relabel: + ha_hello_world: + id: hellow_world + name: "Hello World" From b2199d54642fdf2372fda9a6329a0b507a4446ca Mon Sep 17 00:00:00 2001 From: andyboeh Date: Wed, 31 Aug 2022 00:07:40 +0200 Subject: [PATCH 28/66] mcp23017: read output latch registers during setup (#3744) --- esphome/components/mcp23008/mcp23008.cpp | 3 +++ esphome/components/mcp23016/mcp23016.cpp | 4 ++++ esphome/components/mcp23017/mcp23017.cpp | 4 ++++ esphome/components/mcp23s08/mcp23s08.cpp | 3 +++ esphome/components/mcp23s17/mcp23s17.cpp | 4 ++++ 5 files changed, 18 insertions(+) diff --git a/esphome/components/mcp23008/mcp23008.cpp b/esphome/components/mcp23008/mcp23008.cpp index 351360fe1c..24b9a63f48 100644 --- a/esphome/components/mcp23008/mcp23008.cpp +++ b/esphome/components/mcp23008/mcp23008.cpp @@ -14,6 +14,9 @@ void MCP23008::setup() { return; } + // Read current output register state + this->read_reg(mcp23x08_base::MCP23X08_OLAT, &this->olat_); + if (this->open_drain_ints_) { // enable open-drain interrupt pins, 3.3V-safe this->write_reg(mcp23x08_base::MCP23X08_IOCON, 0x04); diff --git a/esphome/components/mcp23016/mcp23016.cpp b/esphome/components/mcp23016/mcp23016.cpp index 9787da6faa..70df0dfb7b 100644 --- a/esphome/components/mcp23016/mcp23016.cpp +++ b/esphome/components/mcp23016/mcp23016.cpp @@ -15,6 +15,10 @@ void MCP23016::setup() { return; } + // Read current output register state + this->read_reg_(MCP23016_OLAT0, &this->olat_0_); + this->read_reg_(MCP23016_OLAT1, &this->olat_1_); + // all pins input this->write_reg_(MCP23016_IODIR0, 0xFF); this->write_reg_(MCP23016_IODIR1, 0xFF); diff --git a/esphome/components/mcp23017/mcp23017.cpp b/esphome/components/mcp23017/mcp23017.cpp index 7344f482e0..b82e750eaf 100644 --- a/esphome/components/mcp23017/mcp23017.cpp +++ b/esphome/components/mcp23017/mcp23017.cpp @@ -14,6 +14,10 @@ void MCP23017::setup() { return; } + // Read current output register state + this->read_reg(mcp23x17_base::MCP23X17_OLATA, &this->olat_a_); + this->read_reg(mcp23x17_base::MCP23X17_OLATB, &this->olat_b_); + if (this->open_drain_ints_) { // enable open-drain interrupt pins, 3.3V-safe this->write_reg(mcp23x17_base::MCP23X17_IOCONA, 0x04); diff --git a/esphome/components/mcp23s08/mcp23s08.cpp b/esphome/components/mcp23s08/mcp23s08.cpp index af834b4c40..2f885743de 100644 --- a/esphome/components/mcp23s08/mcp23s08.cpp +++ b/esphome/components/mcp23s08/mcp23s08.cpp @@ -23,6 +23,9 @@ void MCP23S08::setup() { this->transfer_byte(0b00011000); // Enable HAEN pins for addressing this->disable(); + // Read current output register state + this->read_reg(mcp23x08_base::MCP23X08_OLAT, &this->olat_); + if (this->open_drain_ints_) { // enable open-drain interrupt pins, 3.3V-safe this->write_reg(mcp23x08_base::MCP23X08_IOCON, 0x04); diff --git a/esphome/components/mcp23s17/mcp23s17.cpp b/esphome/components/mcp23s17/mcp23s17.cpp index 8e3d5213f8..2fc9c74634 100644 --- a/esphome/components/mcp23s17/mcp23s17.cpp +++ b/esphome/components/mcp23s17/mcp23s17.cpp @@ -23,6 +23,10 @@ void MCP23S17::setup() { this->transfer_byte(0b00011000); // Enable HAEN pins for addressing this->disable(); + // Read current output register state + this->read_reg(mcp23x17_base::MCP23X17_OLATA, &this->olat_a_); + this->read_reg(mcp23x17_base::MCP23X17_OLATB, &this->olat_b_); + if (this->open_drain_ints_) { // enable open-drain interrupt pins, 3.3V-safe this->write_reg(mcp23x17_base::MCP23X17_IOCONA, 0x04); From 3120a0ba83a50cddd798ce1f8449b59f5f405615 Mon Sep 17 00:00:00 2001 From: anatoly-savchenkov <48646998+anatoly-savchenkov@users.noreply.github.com> Date: Wed, 31 Aug 2022 01:19:43 +0300 Subject: [PATCH 29/66] Captive portal show nearby WiFi with no ssid configured (#3748) --- esphome/components/wifi/wifi_component.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 10bf4a8f9c..37f5276c8f 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -73,8 +73,11 @@ void WiFiComponent::setup() { ESP_LOGV(TAG, "Setting Output Power Option failed!"); } #ifdef USE_CAPTIVE_PORTAL - if (captive_portal::global_captive_portal != nullptr) + if (captive_portal::global_captive_portal != nullptr) { + this->wifi_sta_pre_setup_(); + this->start_scanning(); captive_portal::global_captive_portal->start(); + } #endif } #ifdef USE_IMPROV From bd6bc283b627842590340961fd30f8aadb1c133c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 31 Aug 2022 11:45:06 +1200 Subject: [PATCH 30/66] Remove unnecessary schema extension on template button (#3753) --- esphome/components/template/button/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/template/button/__init__.py b/esphome/components/template/button/__init__.py index a8bf595942..2ad5e54c80 100644 --- a/esphome/components/template/button/__init__.py +++ b/esphome/components/template/button/__init__.py @@ -9,7 +9,7 @@ CONFIG_SCHEMA = button.BUTTON_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(TemplateButton), } -).extend(cv.COMPONENT_SCHEMA) +) async def to_code(config): From 4d66fab3605bae8c2f9a8cf2278aac03d7d3383f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 31 Aug 2022 13:43:46 +1200 Subject: [PATCH 31/66] Tidy up switch schemas (#3754) --- .../components/ble_client/switch/__init__.py | 15 ++---- esphome/components/copy/switch/__init__.py | 19 ++++---- esphome/components/custom/switch/__init__.py | 8 +--- esphome/components/demo/__init__.py | 11 +---- esphome/components/gpio/switch/__init__.py | 34 ++++++------- .../modbus_controller/switch/__init__.py | 4 +- esphome/components/nextion/switch/__init__.py | 6 +-- esphome/components/output/switch/__init__.py | 26 +++++----- .../components/pipsolar/switch/__init__.py | 20 ++------ esphome/components/restart/switch/__init__.py | 23 +++------ .../components/safe_mode/switch/__init__.py | 30 +++++------- .../components/shutdown/switch/__init__.py | 23 +++------ esphome/components/sprinkler/__init__.py | 48 +++---------------- esphome/components/switch/__init__.py | 14 +++++- .../components/template/switch/__init__.py | 10 ++-- esphome/components/tuya/switch/__init__.py | 22 +++++---- esphome/components/uart/switch/__init__.py | 12 ++--- 17 files changed, 118 insertions(+), 207 deletions(-) diff --git a/esphome/components/ble_client/switch/__init__.py b/esphome/components/ble_client/switch/__init__.py index e5b5ab281b..2304d65c01 100644 --- a/esphome/components/ble_client/switch/__init__.py +++ b/esphome/components/ble_client/switch/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import switch, ble_client -from esphome.const import CONF_ICON, CONF_ID, CONF_INVERTED, ICON_BLUETOOTH +from esphome.const import ICON_BLUETOOTH from .. import ble_client_ns BLEClientSwitch = ble_client_ns.class_( @@ -9,22 +9,13 @@ BLEClientSwitch = ble_client_ns.class_( ) CONFIG_SCHEMA = ( - switch.SWITCH_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(BLEClientSwitch), - cv.Optional(CONF_INVERTED): cv.invalid( - "BLE client switches do not support inverted mode!" - ), - cv.Optional(CONF_ICON, default=ICON_BLUETOOTH): switch.icon, - } - ) + switch.switch_schema(BLEClientSwitch, icon=ICON_BLUETOOTH, block_inverted=True) .extend(ble_client.BLE_CLIENT_SCHEMA) .extend(cv.COMPONENT_SCHEMA) ) async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) + var = await switch.new_switch(config) await cg.register_component(var, config) - await switch.register_switch(var, config) await ble_client.register_ble_node(var, config) diff --git a/esphome/components/copy/switch/__init__.py b/esphome/components/copy/switch/__init__.py index 6622412123..beffbe7fbb 100644 --- a/esphome/components/copy/switch/__init__.py +++ b/esphome/components/copy/switch/__init__.py @@ -5,7 +5,6 @@ from esphome.const import ( CONF_DEVICE_CLASS, CONF_ENTITY_CATEGORY, CONF_ICON, - CONF_ID, CONF_SOURCE_ID, ) from esphome.core.entity_helpers import inherit_property_from @@ -15,12 +14,15 @@ from .. import copy_ns CopySwitch = copy_ns.class_("CopySwitch", switch.Switch, cg.Component) -CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(CopySwitch), - cv.Required(CONF_SOURCE_ID): cv.use_id(switch.Switch), - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + switch.switch_schema(CopySwitch) + .extend( + { + cv.Required(CONF_SOURCE_ID): cv.use_id(switch.Switch), + } + ) + .extend(cv.COMPONENT_SCHEMA) +) FINAL_VALIDATE_SCHEMA = cv.All( inherit_property_from(CONF_ICON, CONF_SOURCE_ID), @@ -30,8 +32,7 @@ FINAL_VALIDATE_SCHEMA = cv.All( async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - await switch.register_switch(var, config) + var = await switch.new_switch(config) await cg.register_component(var, config) source = await cg.get_variable(config[CONF_SOURCE_ID]) diff --git a/esphome/components/custom/switch/__init__.py b/esphome/components/custom/switch/__init__.py index e0b9d7751a..5538ae6aa0 100644 --- a/esphome/components/custom/switch/__init__.py +++ b/esphome/components/custom/switch/__init__.py @@ -10,13 +10,7 @@ CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(CustomSwitchConstructor), cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_SWITCHES): cv.ensure_list( - switch.SWITCH_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(switch.Switch), - } - ) - ), + cv.Required(CONF_SWITCHES): cv.ensure_list(switch.switch_schema(switch.Switch)), } ) diff --git a/esphome/components/demo/__init__.py b/esphome/components/demo/__init__.py index 7aa742919a..19b35828a5 100644 --- a/esphome/components/demo/__init__.py +++ b/esphome/components/demo/__init__.py @@ -349,13 +349,7 @@ CONFIG_SCHEMA = cv.Schema( CONF_ICON: ICON_BLUETOOTH, }, ], - ): [ - switch.SWITCH_SCHEMA.extend(cv.COMPONENT_SCHEMA).extend( - { - cv.GenerateID(): cv.declare_id(DemoSwitch), - } - ) - ], + ): [switch.switch_schema(DemoSwitch).extend(cv.COMPONENT_SCHEMA)], cv.Optional( CONF_TEXT_SENSORS, default=[ @@ -422,9 +416,8 @@ async def to_code(config): await cg.register_component(var, conf) for conf in config[CONF_SWITCHES]: - var = cg.new_Pvariable(conf[CONF_ID]) + var = await switch.new_switch(conf) await cg.register_component(var, conf) - await switch.register_switch(var, conf) for conf in config[CONF_TEXT_SENSORS]: var = await text_sensor.new_text_sensor(conf) diff --git a/esphome/components/gpio/switch/__init__.py b/esphome/components/gpio/switch/__init__.py index a03e16a2c1..f81cd5b6fb 100644 --- a/esphome/components/gpio/switch/__init__.py +++ b/esphome/components/gpio/switch/__init__.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import switch -from esphome.const import CONF_ID, CONF_INTERLOCK, CONF_PIN, CONF_RESTORE_MODE +from esphome.const import CONF_INTERLOCK, CONF_PIN, CONF_RESTORE_MODE from .. import gpio_ns GPIOSwitch = gpio_ns.class_("GPIOSwitch", switch.Switch, cg.Component) @@ -18,25 +18,27 @@ RESTORE_MODES = { } CONF_INTERLOCK_WAIT_TIME = "interlock_wait_time" -CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(GPIOSwitch), - cv.Required(CONF_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_RESTORE_MODE, default="RESTORE_DEFAULT_OFF"): cv.enum( - RESTORE_MODES, upper=True, space="_" - ), - cv.Optional(CONF_INTERLOCK): cv.ensure_list(cv.use_id(switch.Switch)), - cv.Optional( - CONF_INTERLOCK_WAIT_TIME, default="0ms" - ): cv.positive_time_period_milliseconds, - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + switch.switch_schema(GPIOSwitch) + .extend( + { + cv.Required(CONF_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_RESTORE_MODE, default="RESTORE_DEFAULT_OFF"): cv.enum( + RESTORE_MODES, upper=True, space="_" + ), + cv.Optional(CONF_INTERLOCK): cv.ensure_list(cv.use_id(switch.Switch)), + cv.Optional( + CONF_INTERLOCK_WAIT_TIME, default="0ms" + ): cv.positive_time_period_milliseconds, + } + ) + .extend(cv.COMPONENT_SCHEMA) +) async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) + var = await switch.new_switch(config) await cg.register_component(var, config) - await switch.register_switch(var, config) pin = await cg.gpio_pin_expression(config[CONF_PIN]) cg.add(var.set_pin(pin)) diff --git a/esphome/components/modbus_controller/switch/__init__.py b/esphome/components/modbus_controller/switch/__init__.py index 0dfbd83cb8..9673a066e3 100644 --- a/esphome/components/modbus_controller/switch/__init__.py +++ b/esphome/components/modbus_controller/switch/__init__.py @@ -32,11 +32,11 @@ ModbusSwitch = modbus_controller_ns.class_( ) CONFIG_SCHEMA = cv.All( - switch.SWITCH_SCHEMA.extend(cv.COMPONENT_SCHEMA) + switch.switch_schema(ModbusSwitch) + .extend(cv.COMPONENT_SCHEMA) .extend(ModbusItemBaseSchema) .extend( { - cv.GenerateID(): cv.declare_id(ModbusSwitch), cv.Optional(CONF_REGISTER_TYPE): cv.enum(MODBUS_REGISTER_TYPE), cv.Optional(CONF_USE_WRITE_MULTIPLE, default=False): cv.boolean, cv.Optional(CONF_WRITE_LAMBDA): cv.returning_lambda, diff --git a/esphome/components/nextion/switch/__init__.py b/esphome/components/nextion/switch/__init__.py index 068681fa14..91ab0cc81f 100644 --- a/esphome/components/nextion/switch/__init__.py +++ b/esphome/components/nextion/switch/__init__.py @@ -17,11 +17,7 @@ CODEOWNERS = ["@senexcrenshaw"] NextionSwitch = nextion_ns.class_("NextionSwitch", switch.Switch, cg.PollingComponent) CONFIG_SCHEMA = cv.All( - switch.SWITCH_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(NextionSwitch), - } - ) + switch.switch_schema(NextionSwitch) .extend(CONFIG_SWITCH_COMPONENT_SCHEMA) .extend(cv.polling_component_schema("never")), cv.has_exactly_one_key(CONF_COMPONENT_NAME, CONF_VARIABLE_NAME), diff --git a/esphome/components/output/switch/__init__.py b/esphome/components/output/switch/__init__.py index 46135d117e..3a23c1e33f 100644 --- a/esphome/components/output/switch/__init__.py +++ b/esphome/components/output/switch/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import output, switch -from esphome.const import CONF_ID, CONF_OUTPUT, CONF_RESTORE_MODE +from esphome.const import CONF_OUTPUT, CONF_RESTORE_MODE from .. import output_ns OutputSwitch = output_ns.class_("OutputSwitch", switch.Switch, cg.Component) @@ -16,21 +16,23 @@ RESTORE_MODES = { "RESTORE_INVERTED_DEFAULT_ON": OutputSwitchRestoreMode.OUTPUT_SWITCH_RESTORE_INVERTED_DEFAULT_ON, } -CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(OutputSwitch), - cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), - cv.Optional(CONF_RESTORE_MODE, default="RESTORE_DEFAULT_OFF"): cv.enum( - RESTORE_MODES, upper=True, space="_" - ), - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + switch.switch_schema(OutputSwitch) + .extend( + { + cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), + cv.Optional(CONF_RESTORE_MODE, default="RESTORE_DEFAULT_OFF"): cv.enum( + RESTORE_MODES, upper=True, space="_" + ), + } + ) + .extend(cv.COMPONENT_SCHEMA) +) async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) + var = await switch.new_switch(config) await cg.register_component(var, config) - await switch.register_switch(var, config) output_ = await cg.get_variable(config[CONF_OUTPUT]) cg.add(var.set_output(output_)) diff --git a/esphome/components/pipsolar/switch/__init__.py b/esphome/components/pipsolar/switch/__init__.py index 5ff33b10ff..7658c7d4f8 100644 --- a/esphome/components/pipsolar/switch/__init__.py +++ b/esphome/components/pipsolar/switch/__init__.py @@ -1,12 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import switch -from esphome.const import ( - CONF_ID, - CONF_INVERTED, - CONF_ICON, - ICON_POWER, -) +from esphome.const import ICON_POWER from .. import CONF_PIPSOLAR_ID, PIPSOLAR_COMPONENT_SCHEMA, pipsolar_ns DEPENDENCIES = ["uart"] @@ -29,14 +24,8 @@ TYPES = { PipsolarSwitch = pipsolar_ns.class_("PipsolarSwitch", switch.Switch, cg.Component) -PIPSWITCH_SCHEMA = switch.SWITCH_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(PipsolarSwitch), - cv.Optional(CONF_INVERTED): cv.invalid( - "Pipsolar switches do not support inverted mode!" - ), - cv.Optional(CONF_ICON, default=ICON_POWER): switch.icon, - } +PIPSWITCH_SCHEMA = switch.switch_schema( + PipsolarSwitch, icon=ICON_POWER, block_inverted=True ).extend(cv.COMPONENT_SCHEMA) CONFIG_SCHEMA = PIPSOLAR_COMPONENT_SCHEMA.extend( @@ -50,9 +39,8 @@ async def to_code(config): for type, (on, off) in TYPES.items(): if type in config: conf = config[type] - var = cg.new_Pvariable(conf[CONF_ID]) + var = await switch.new_switch(conf) await cg.register_component(var, conf) - await switch.register_switch(var, conf) cg.add(getattr(paren, f"set_{type}_switch")(var)) cg.add(var.set_parent(paren)) cg.add(var.set_on_command(on)) diff --git a/esphome/components/restart/switch/__init__.py b/esphome/components/restart/switch/__init__.py index de30392b45..89805b4246 100644 --- a/esphome/components/restart/switch/__init__.py +++ b/esphome/components/restart/switch/__init__.py @@ -2,10 +2,6 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import switch from esphome.const import ( - CONF_ENTITY_CATEGORY, - CONF_ID, - CONF_INVERTED, - CONF_ICON, ENTITY_CATEGORY_CONFIG, ICON_RESTART, ) @@ -13,21 +9,14 @@ from esphome.const import ( restart_ns = cg.esphome_ns.namespace("restart") RestartSwitch = restart_ns.class_("RestartSwitch", switch.Switch, cg.Component) -CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(RestartSwitch), - cv.Optional(CONF_INVERTED): cv.invalid( - "Restart switches do not support inverted mode!" - ), - cv.Optional(CONF_ICON, default=ICON_RESTART): switch.icon, - cv.Optional( - CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_CONFIG - ): cv.entity_category, - } +CONFIG_SCHEMA = switch.switch_schema( + RestartSwitch, + icon=ICON_RESTART, + entity_category=ENTITY_CATEGORY_CONFIG, + block_inverted=True, ).extend(cv.COMPONENT_SCHEMA) async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) + var = await switch.new_switch(config) await cg.register_component(var, config) - await switch.register_switch(var, config) diff --git a/esphome/components/safe_mode/switch/__init__.py b/esphome/components/safe_mode/switch/__init__.py index b6c3e852f6..a6fcdfbece 100644 --- a/esphome/components/safe_mode/switch/__init__.py +++ b/esphome/components/safe_mode/switch/__init__.py @@ -3,10 +3,6 @@ import esphome.config_validation as cv from esphome.components import switch from esphome.components.ota import OTAComponent from esphome.const import ( - CONF_ENTITY_CATEGORY, - CONF_ID, - CONF_INVERTED, - CONF_ICON, CONF_OTA, ENTITY_CATEGORY_CONFIG, ICON_RESTART_ALERT, @@ -17,25 +13,21 @@ DEPENDENCIES = ["ota"] SafeModeSwitch = safe_mode_ns.class_("SafeModeSwitch", switch.Switch, cg.Component) -CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(SafeModeSwitch), - cv.GenerateID(CONF_OTA): cv.use_id(OTAComponent), - cv.Optional(CONF_INVERTED): cv.invalid( - "Safe Mode Restart switches do not support inverted mode!" - ), - cv.Optional(CONF_ICON, default=ICON_RESTART_ALERT): switch.icon, - cv.Optional( - CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_CONFIG - ): cv.entity_category, - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + switch.switch_schema( + SafeModeSwitch, + icon=ICON_RESTART_ALERT, + entity_category=ENTITY_CATEGORY_CONFIG, + block_inverted=True, + ) + .extend({cv.GenerateID(CONF_OTA): cv.use_id(OTAComponent)}) + .extend(cv.COMPONENT_SCHEMA) +) async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) + var = await switch.new_switch(config) await cg.register_component(var, config) - await switch.register_switch(var, config) ota = await cg.get_variable(config[CONF_OTA]) cg.add(var.set_ota(ota)) diff --git a/esphome/components/shutdown/switch/__init__.py b/esphome/components/shutdown/switch/__init__.py index 49970b4c2f..5de9f2d189 100644 --- a/esphome/components/shutdown/switch/__init__.py +++ b/esphome/components/shutdown/switch/__init__.py @@ -2,10 +2,6 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import switch from esphome.const import ( - CONF_ENTITY_CATEGORY, - CONF_ID, - CONF_INVERTED, - CONF_ICON, ENTITY_CATEGORY_CONFIG, ICON_POWER, ) @@ -13,21 +9,14 @@ from esphome.const import ( shutdown_ns = cg.esphome_ns.namespace("shutdown") ShutdownSwitch = shutdown_ns.class_("ShutdownSwitch", switch.Switch, cg.Component) -CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(ShutdownSwitch), - cv.Optional(CONF_INVERTED): cv.invalid( - "Shutdown switches do not support inverted mode!" - ), - cv.Optional(CONF_ICON, default=ICON_POWER): switch.icon, - cv.Optional( - CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_CONFIG - ): cv.entity_category, - } +CONFIG_SCHEMA = switch.switch_schema( + ShutdownSwitch, + icon=ICON_POWER, + entity_category=ENTITY_CATEGORY_CONFIG, + block_inverted=True, ).extend(cv.COMPONENT_SCHEMA) async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) + var = await switch.new_switch(config) await cg.register_component(var, config) - await switch.register_switch(var, config) diff --git a/esphome/components/sprinkler/__init__.py b/esphome/components/sprinkler/__init__.py index 659eb5b58e..4e80cfa021 100644 --- a/esphome/components/sprinkler/__init__.py +++ b/esphome/components/sprinkler/__init__.py @@ -223,13 +223,7 @@ SPRINKLER_ACTION_QUEUE_VALVE_SCHEMA = cv.Schema( SPRINKLER_VALVE_SCHEMA = cv.Schema( { cv.Optional(CONF_ENABLE_SWITCH): cv.maybe_simple_value( - switch.SWITCH_SCHEMA.extend( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(SprinklerControllerSwitch), - } - ) - ), + switch.switch_schema(SprinklerControllerSwitch), key=CONF_NAME, ), cv.Optional(CONF_PUMP_OFF_SWITCH_ID): cv.use_id(switch.Switch), @@ -237,13 +231,7 @@ SPRINKLER_VALVE_SCHEMA = cv.Schema( cv.Optional(CONF_PUMP_SWITCH_ID): cv.use_id(switch.Switch), cv.Required(CONF_RUN_DURATION): cv.positive_time_period_seconds, cv.Required(CONF_VALVE_SWITCH): cv.maybe_simple_value( - switch.SWITCH_SCHEMA.extend( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(SprinklerControllerSwitch), - } - ) - ), + switch.switch_schema(SprinklerControllerSwitch), key=CONF_NAME, ), cv.Optional(CONF_VALVE_OFF_SWITCH_ID): cv.use_id(switch.Switch), @@ -256,43 +244,19 @@ SPRINKLER_CONTROLLER_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(Sprinkler), cv.Optional(CONF_AUTO_ADVANCE_SWITCH): cv.maybe_simple_value( - switch.SWITCH_SCHEMA.extend( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(SprinklerControllerSwitch), - } - ) - ), + switch.switch_schema(SprinklerControllerSwitch), key=CONF_NAME, ), cv.Optional(CONF_MAIN_SWITCH): cv.maybe_simple_value( - switch.SWITCH_SCHEMA.extend( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(SprinklerControllerSwitch), - } - ) - ), + switch.switch_schema(SprinklerControllerSwitch), key=CONF_NAME, ), cv.Optional(CONF_QUEUE_ENABLE_SWITCH): cv.maybe_simple_value( - switch.SWITCH_SCHEMA.extend( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(SprinklerControllerSwitch), - } - ) - ), + switch.switch_schema(SprinklerControllerSwitch), key=CONF_NAME, ), cv.Optional(CONF_REVERSE_SWITCH): cv.maybe_simple_value( - switch.SWITCH_SCHEMA.extend( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(SprinklerControllerSwitch), - } - ) - ), + switch.switch_schema(SprinklerControllerSwitch), key=CONF_NAME, ), cv.Optional(CONF_MANUAL_SELECTION_DELAY): cv.positive_time_period_seconds, diff --git a/esphome/components/switch/__init__.py b/esphome/components/switch/__init__.py index 54ad2b852e..336c7d38d6 100644 --- a/esphome/components/switch/__init__.py +++ b/esphome/components/switch/__init__.py @@ -6,6 +6,7 @@ from esphome.components import mqtt from esphome.const import ( CONF_DEVICE_CLASS, CONF_ENTITY_CATEGORY, + CONF_ICON, CONF_ID, CONF_INVERTED, CONF_MQTT_ID, @@ -45,7 +46,6 @@ SwitchTurnOffTrigger = switch_ns.class_( "SwitchTurnOffTrigger", automation.Trigger.template() ) -icon = cv.icon validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True) @@ -76,6 +76,8 @@ def switch_schema( *, entity_category: str = _UNDEF, device_class: str = _UNDEF, + icon: str = _UNDEF, + block_inverted: bool = False, ): schema = SWITCH_SCHEMA if class_ is not _UNDEF: @@ -96,6 +98,16 @@ def switch_schema( ): validate_device_class } ) + if icon is not _UNDEF: + schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon}) + if block_inverted: + schema = schema.extend( + { + cv.Optional(CONF_INVERTED): cv.invalid( + "Inverted is not supported for this platform!" + ) + } + ) return schema diff --git a/esphome/components/template/switch/__init__.py b/esphome/components/template/switch/__init__.py index 6095a7c561..e002c4e3d8 100644 --- a/esphome/components/template/switch/__init__.py +++ b/esphome/components/template/switch/__init__.py @@ -31,9 +31,9 @@ def validate(config): CONFIG_SCHEMA = cv.All( - switch.SWITCH_SCHEMA.extend( + switch.switch_schema(TemplateSwitch) + .extend( { - cv.GenerateID(): cv.declare_id(TemplateSwitch), cv.Optional(CONF_LAMBDA): cv.returning_lambda, cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean, @@ -45,15 +45,15 @@ CONFIG_SCHEMA = cv.All( ), cv.Optional(CONF_RESTORE_STATE, default=False): cv.boolean, } - ).extend(cv.COMPONENT_SCHEMA), + ) + .extend(cv.COMPONENT_SCHEMA), validate, ) async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) + var = await switch.new_switch(config) await cg.register_component(var, config) - await switch.register_switch(var, config) if CONF_LAMBDA in config: template_ = await cg.process_lambda( diff --git a/esphome/components/tuya/switch/__init__.py b/esphome/components/tuya/switch/__init__.py index 4df6bba713..b4ec53ec59 100644 --- a/esphome/components/tuya/switch/__init__.py +++ b/esphome/components/tuya/switch/__init__.py @@ -1,7 +1,7 @@ from esphome.components import switch import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ID, CONF_SWITCH_DATAPOINT +from esphome.const import CONF_SWITCH_DATAPOINT from .. import tuya_ns, CONF_TUYA_ID, Tuya DEPENDENCIES = ["tuya"] @@ -9,19 +9,21 @@ CODEOWNERS = ["@jesserockz"] TuyaSwitch = tuya_ns.class_("TuyaSwitch", switch.Switch, cg.Component) -CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(TuyaSwitch), - cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), - cv.Required(CONF_SWITCH_DATAPOINT): cv.uint8_t, - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + switch.switch_schema(TuyaSwitch) + .extend( + { + cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), + cv.Required(CONF_SWITCH_DATAPOINT): cv.uint8_t, + } + ) + .extend(cv.COMPONENT_SCHEMA) +) async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) + var = await switch.new_switch(config) await cg.register_component(var, config) - await switch.register_switch(var, config) paren = await cg.get_variable(config[CONF_TUYA_ID]) cg.add(var.set_tuya_parent(paren)) diff --git a/esphome/components/uart/switch/__init__.py b/esphome/components/uart/switch/__init__.py index 9e7f95bd2a..60f5ddaf0d 100644 --- a/esphome/components/uart/switch/__init__.py +++ b/esphome/components/uart/switch/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import switch, uart -from esphome.const import CONF_DATA, CONF_ID, CONF_INVERTED, CONF_SEND_EVERY +from esphome.const import CONF_DATA, CONF_SEND_EVERY from esphome.core import HexInt from .. import uart_ns, validate_raw_data @@ -11,13 +11,10 @@ UARTSwitch = uart_ns.class_("UARTSwitch", switch.Switch, uart.UARTDevice, cg.Com CONFIG_SCHEMA = ( - switch.SWITCH_SCHEMA.extend( + switch.switch_schema(UARTSwitch, block_inverted=True) + .extend( { - cv.GenerateID(): cv.declare_id(UARTSwitch), cv.Required(CONF_DATA): validate_raw_data, - cv.Optional(CONF_INVERTED): cv.invalid( - "UART switches do not support inverted mode!" - ), cv.Optional(CONF_SEND_EVERY): cv.positive_time_period_milliseconds, } ) @@ -27,9 +24,8 @@ CONFIG_SCHEMA = ( async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) + var = await switch.new_switch(config) await cg.register_component(var, config) - await switch.register_switch(var, config) await uart.register_uart_device(var, config) data = config[CONF_DATA] From 768490089ea5ee0fa9669aa63eea08d0a4adfd13 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 30 Aug 2022 20:45:30 -0500 Subject: [PATCH 32/66] Add IP101 support to Ethernet component (#3751) --- esphome/components/ethernet/__init__.py | 1 + .../ethernet/ethernet_component.cpp | 39 ++++++++++++++++++- .../components/ethernet/ethernet_component.h | 1 + 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index bbf64a3cd1..696cc62e75 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -31,6 +31,7 @@ EthernetType = ethernet_ns.enum("EthernetType") ETHERNET_TYPES = { "LAN8720": EthernetType.ETHERNET_TYPE_LAN8720, "TLK110": EthernetType.ETHERNET_TYPE_TLK110, + "IP101": EthernetType.ETHERNET_TYPE_IP101, } eth_clock_mode_t = cg.global_ns.enum("eth_clock_mode_t") diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 384a31ed2f..d768964017 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -6,6 +6,7 @@ #ifdef USE_ESP32_FRAMEWORK_ARDUINO #include +#include #include #include @@ -33,6 +34,7 @@ EthernetComponent *global_eth_component; // NOLINT(cppcoreguidelines-avoid-non- } EthernetComponent::EthernetComponent() { global_eth_component = this; } + void EthernetComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up Ethernet..."); @@ -52,6 +54,10 @@ void EthernetComponent::setup() { memcpy(&this->eth_config_, &phy_tlk110_default_ethernet_config, sizeof(eth_config_t)); break; } + case ETHERNET_TYPE_IP101: { + memcpy(&this->eth_config_, &phy_ip101_default_ethernet_config, sizeof(eth_config_t)); + break; + } default: { this->mark_failed(); return; @@ -76,6 +82,7 @@ void EthernetComponent::setup() { err = esp_eth_enable(); ESPHL_ERROR_CHECK(err, "ETH enable error"); } + void EthernetComponent::loop() { const uint32_t now = millis(); @@ -115,16 +122,39 @@ void EthernetComponent::loop() { break; } } + void EthernetComponent::dump_config() { + std::string eth_type; + switch (this->type_) { + case ETHERNET_TYPE_LAN8720: + eth_type = "LAN8720"; + break; + + case ETHERNET_TYPE_TLK110: + eth_type = "TLK110"; + break; + + case ETHERNET_TYPE_IP101: + eth_type = "IP101"; + break; + + default: + eth_type = "Unknown"; + break; + } + ESP_LOGCONFIG(TAG, "Ethernet:"); this->dump_connect_params_(); LOG_PIN(" Power Pin: ", this->power_pin_); ESP_LOGCONFIG(TAG, " MDC Pin: %u", this->mdc_pin_); ESP_LOGCONFIG(TAG, " MDIO Pin: %u", this->mdio_pin_); - ESP_LOGCONFIG(TAG, " Type: %s", this->type_ == ETHERNET_TYPE_LAN8720 ? "LAN8720" : "TLK110"); + ESP_LOGCONFIG(TAG, " Type: %s", eth_type.c_str()); } + float EthernetComponent::get_setup_priority() const { return setup_priority::WIFI; } + bool EthernetComponent::can_proceed() { return this->is_connected(); } + network::IPAddress EthernetComponent::get_ip_address() { tcpip_adapter_ip_info_t ip; tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip); @@ -213,17 +243,21 @@ void EthernetComponent::start_connect_() { this->connect_begin_ = millis(); this->status_set_warning(); } + void EthernetComponent::eth_phy_config_gpio() { phy_rmii_configure_data_interface_pins(); phy_rmii_smi_configure_pins(global_eth_component->mdc_pin_, global_eth_component->mdio_pin_); } + void EthernetComponent::eth_phy_power_enable(bool enable) { global_eth_component->power_pin_->digital_write(enable); // power up takes some time, datasheet says max 300µs delay(1); global_eth_component->orig_power_enable_fun_(enable); } + bool EthernetComponent::is_connected() { return this->state_ == EthernetComponentState::CONNECTED; } + void EthernetComponent::dump_connect_params_() { tcpip_adapter_ip_info_t ip; tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip); @@ -250,6 +284,7 @@ void EthernetComponent::dump_connect_params_() { ESP_LOGCONFIG(TAG, " Link Up: %s", YESNO(this->eth_config_.phy_check_link())); ESP_LOGCONFIG(TAG, " Link Speed: %u", this->eth_config_.phy_get_speed_mode() ? 100 : 10); } + void EthernetComponent::set_phy_addr(uint8_t phy_addr) { this->phy_addr_ = phy_addr; } void EthernetComponent::set_power_pin(GPIOPin *power_pin) { this->power_pin_ = power_pin; } void EthernetComponent::set_mdc_pin(uint8_t mdc_pin) { this->mdc_pin_ = mdc_pin; } @@ -257,12 +292,14 @@ void EthernetComponent::set_mdio_pin(uint8_t mdio_pin) { this->mdio_pin_ = mdio_ void EthernetComponent::set_type(EthernetType type) { this->type_ = type; } void EthernetComponent::set_clk_mode(eth_clock_mode_t clk_mode) { this->clk_mode_ = clk_mode; } void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_ip_ = manual_ip; } + std::string EthernetComponent::get_use_address() const { if (this->use_address_.empty()) { return App.get_name() + ".local"; } return this->use_address_; } + void EthernetComponent::set_use_address(const std::string &use_address) { this->use_address_ = use_address; } } // namespace ethernet diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index abe1c62030..0924405521 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -17,6 +17,7 @@ namespace ethernet { enum EthernetType { ETHERNET_TYPE_LAN8720 = 0, ETHERNET_TYPE_TLK110, + ETHERNET_TYPE_IP101, }; struct ManualIP { From 6d5cb866dbec1356379683854f6ee685190c096d Mon Sep 17 00:00:00 2001 From: Joe Date: Tue, 30 Aug 2022 21:53:18 -0400 Subject: [PATCH 33/66] Add BedJet Fan child component (#3735) --- CODEOWNERS | 2 + esphome/components/bedjet/bedjet_const.h | 6 +- .../{climate.py => climate/__init__.py} | 10 +- .../bedjet/{ => climate}/bedjet_climate.cpp | 4 +- .../bedjet/{ => climate}/bedjet_climate.h | 8 +- esphome/components/bedjet/fan/__init__.py | 36 ++++++ esphome/components/bedjet/fan/bedjet_fan.cpp | 108 ++++++++++++++++++ esphome/components/bedjet/fan/bedjet_fan.h | 40 +++++++ tests/test1.yaml | 3 + 9 files changed, 203 insertions(+), 14 deletions(-) rename esphome/components/bedjet/{climate.py => climate/__init__.py} (89%) rename esphome/components/bedjet/{ => climate}/bedjet_climate.cpp (99%) rename esphome/components/bedjet/{ => climate}/bedjet_climate.h (95%) create mode 100644 esphome/components/bedjet/fan/__init__.py create mode 100644 esphome/components/bedjet/fan/bedjet_fan.cpp create mode 100644 esphome/components/bedjet/fan/bedjet_fan.h diff --git a/CODEOWNERS b/CODEOWNERS index fe6e712d45..ae112b3330 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -29,6 +29,8 @@ esphome/components/b_parasite/* @rbaron esphome/components/ballu/* @bazuchan esphome/components/bang_bang/* @OttoWinter esphome/components/bedjet/* @jhansche +esphome/components/bedjet/climate/* @jhansche +esphome/components/bedjet/fan/* @jhansche esphome/components/bh1750/* @OttoWinter esphome/components/binary_sensor/* @esphome/core esphome/components/bl0939/* @ziceva diff --git a/esphome/components/bedjet/bedjet_const.h b/esphome/components/bedjet/bedjet_const.h index bd2fb2421d..27a75b2671 100644 --- a/esphome/components/bedjet/bedjet_const.h +++ b/esphome/components/bedjet/bedjet_const.h @@ -89,8 +89,10 @@ enum BedjetCommand : uint8_t { "85%", "90%", "95%", "100%" \ } -static const char *const BEDJET_FAN_STEP_NAMES[20] = BEDJET_FAN_STEP_NAMES_; -static const std::string BEDJET_FAN_STEP_NAME_STRINGS[20] = BEDJET_FAN_STEP_NAMES_; +static const uint8_t BEDJET_FAN_SPEED_COUNT = 20; + +static const char *const BEDJET_FAN_STEP_NAMES[BEDJET_FAN_SPEED_COUNT] = BEDJET_FAN_STEP_NAMES_; +static const std::string BEDJET_FAN_STEP_NAME_STRINGS[BEDJET_FAN_SPEED_COUNT] = BEDJET_FAN_STEP_NAMES_; static const std::set BEDJET_FAN_STEP_NAMES_SET BEDJET_FAN_STEP_NAMES_; } // namespace bedjet diff --git a/esphome/components/bedjet/climate.py b/esphome/components/bedjet/climate/__init__.py similarity index 89% rename from esphome/components/bedjet/climate.py rename to esphome/components/bedjet/climate/__init__.py index 9865cd716d..b12622868f 100644 --- a/esphome/components/bedjet/climate.py +++ b/esphome/components/bedjet/climate/__init__.py @@ -9,19 +9,17 @@ from esphome.const import ( CONF_RECEIVE_TIMEOUT, CONF_TIME_ID, ) -from . import ( +from .. import ( BEDJET_CLIENT_SCHEMA, + bedjet_ns, register_bedjet_child, ) _LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@jhansche"] -DEPENDENCIES = ["ble_client"] +DEPENDENCIES = ["bedjet"] -bedjet_ns = cg.esphome_ns.namespace("bedjet") -BedJetClimate = bedjet_ns.class_( - "BedJetClimate", climate.Climate, ble_client.BLEClientNode, cg.PollingComponent -) +BedJetClimate = bedjet_ns.class_("BedJetClimate", climate.Climate, cg.PollingComponent) BedjetHeatMode = bedjet_ns.enum("BedjetHeatMode") BEDJET_HEAT_MODES = { "heat": BedjetHeatMode.HEAT_MODE_HEAT, diff --git a/esphome/components/bedjet/bedjet_climate.cpp b/esphome/components/bedjet/climate/bedjet_climate.cpp similarity index 99% rename from esphome/components/bedjet/bedjet_climate.cpp rename to esphome/components/bedjet/climate/bedjet_climate.cpp index 8d9fdd7318..431cf614e9 100644 --- a/esphome/components/bedjet/bedjet_climate.cpp +++ b/esphome/components/bedjet/climate/bedjet_climate.cpp @@ -15,13 +15,13 @@ float bedjet_temp_to_c(const uint8_t temp) { } static const std::string *bedjet_fan_step_to_fan_mode(const uint8_t fan_step) { - if (fan_step <= 19) + if (fan_step < BEDJET_FAN_SPEED_COUNT) return &BEDJET_FAN_STEP_NAME_STRINGS[fan_step]; return nullptr; } static uint8_t bedjet_fan_speed_to_step(const std::string &fan_step_percent) { - for (int i = 0; i < sizeof(BEDJET_FAN_STEP_NAME_STRINGS); i++) { + for (int i = 0; i < BEDJET_FAN_SPEED_COUNT; i++) { if (fan_step_percent == BEDJET_FAN_STEP_NAME_STRINGS[i]) { return i; } diff --git a/esphome/components/bedjet/bedjet_climate.h b/esphome/components/bedjet/climate/bedjet_climate.h similarity index 95% rename from esphome/components/bedjet/bedjet_climate.h rename to esphome/components/bedjet/climate/bedjet_climate.h index 27ee5c7501..48c50d842f 100644 --- a/esphome/components/bedjet/bedjet_climate.h +++ b/esphome/components/bedjet/climate/bedjet_climate.h @@ -1,12 +1,12 @@ #pragma once -#include "esphome/components/climate/climate.h" #include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/core/hal.h" -#include "bedjet_child.h" -#include "bedjet_codec.h" -#include "bedjet_hub.h" +#include "esphome/components/bedjet/bedjet_child.h" +#include "esphome/components/bedjet/bedjet_codec.h" +#include "esphome/components/bedjet/bedjet_hub.h" +#include "esphome/components/climate/climate.h" #ifdef USE_ESP32 diff --git a/esphome/components/bedjet/fan/__init__.py b/esphome/components/bedjet/fan/__init__.py new file mode 100644 index 0000000000..06e81ea979 --- /dev/null +++ b/esphome/components/bedjet/fan/__init__.py @@ -0,0 +1,36 @@ +import logging + +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import fan +from esphome.const import ( + CONF_ID, +) +from .. import ( + BEDJET_CLIENT_SCHEMA, + bedjet_ns, + register_bedjet_child, +) + +_LOGGER = logging.getLogger(__name__) +CODEOWNERS = ["@jhansche"] +DEPENDENCIES = ["bedjet"] + +BedJetFan = bedjet_ns.class_("BedJetFan", fan.Fan, cg.PollingComponent) + +CONFIG_SCHEMA = ( + fan.FAN_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(BedJetFan), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(BEDJET_CLIENT_SCHEMA) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await fan.register_fan(var, config) + await register_bedjet_child(var, config) diff --git a/esphome/components/bedjet/fan/bedjet_fan.cpp b/esphome/components/bedjet/fan/bedjet_fan.cpp new file mode 100644 index 0000000000..02ac289e0e --- /dev/null +++ b/esphome/components/bedjet/fan/bedjet_fan.cpp @@ -0,0 +1,108 @@ +#include "bedjet_fan.h" +#include "esphome/core/log.h" + +#ifdef USE_ESP32 + +namespace esphome { +namespace bedjet { + +using namespace esphome::fan; + +void BedJetFan::dump_config() { LOG_FAN("", "BedJet Fan", this); } +std::string BedJetFan::describe() { return "BedJet Fan"; } + +void BedJetFan::control(const fan::FanCall &call) { + ESP_LOGD(TAG, "Received BedJetFan::control"); + if (!this->parent_->is_connected()) { + ESP_LOGW(TAG, "Not connected, cannot handle control call yet."); + return; + } + bool did_change = false; + + if (call.get_state().has_value() && this->state != *call.get_state()) { + // Turning off is easy: + if (this->state && this->parent_->button_off()) { + this->state = false; + this->publish_state(); + return; + } + + // Turning on, we have to choose a specific mode; for now, use "COOL" mode + // In the future we could configure the mode to use for fan.turn_on. + if (this->parent_->button_cool()) { + this->state = true; + did_change = true; + } + } + + // ignore speed changes if not on or turning on + if (this->state && call.get_speed().has_value()) { + this->speed = *call.get_speed(); + this->parent_->set_fan_index(this->speed); + did_change = true; + } + + if (did_change) { + this->publish_state(); + } +} + +void BedJetFan::on_status(const BedjetStatusPacket *data) { + ESP_LOGVV(TAG, "[%s] Handling on_status with data=%p", this->get_name().c_str(), (void *) data); + bool did_change = false; + bool new_state = data->mode != MODE_STANDBY && data->mode != MODE_WAIT; + + if (new_state != this->state) { + this->state = new_state; + did_change = true; + } + + if (data->fan_step != this->speed) { + this->speed = data->fan_step; + did_change = true; + } + + if (did_change) { + this->publish_state(); + } +} + +/** Attempts to update the fan device from the last received BedjetStatusPacket. + * + * This will be called from #on_status() when the parent dispatches new status packets, + * and from #update() when the polling interval is triggered. + * + * @return `true` if the status has been applied; `false` if there is nothing to apply. + */ +bool BedJetFan::update_status_() { + if (!this->parent_->is_connected()) + return false; + if (!this->parent_->has_status()) + return false; + + auto *status = this->parent_->get_status_packet(); + + if (status == nullptr) + return false; + + this->on_status(status); + return true; +} + +void BedJetFan::update() { + ESP_LOGD(TAG, "[%s] update()", this->get_name().c_str()); + // TODO: if the hub component is already polling, do we also need to include polling? + // We're already going to get on_status() at the hub's polling interval. + auto result = this->update_status_(); + ESP_LOGD(TAG, "[%s] update_status result=%s", this->get_name().c_str(), result ? "true" : "false"); +} + +/** Resets states to defaults. */ +void BedJetFan::reset_state_() { + this->state = false; + this->publish_state(); +} +} // namespace bedjet +} // namespace esphome + +#endif diff --git a/esphome/components/bedjet/fan/bedjet_fan.h b/esphome/components/bedjet/fan/bedjet_fan.h new file mode 100644 index 0000000000..19db06e9d3 --- /dev/null +++ b/esphome/components/bedjet/fan/bedjet_fan.h @@ -0,0 +1,40 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/defines.h" +#include "esphome/core/hal.h" +#include "esphome/components/bedjet/bedjet_child.h" +#include "esphome/components/bedjet/bedjet_codec.h" +#include "esphome/components/bedjet/bedjet_hub.h" +#include "esphome/components/fan/fan.h" + +#ifdef USE_ESP32 + +namespace esphome { +namespace bedjet { + +class BedJetFan : public fan::Fan, public BedJetClient, public PollingComponent { + public: + void update() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + + /* BedJetClient status update */ + void on_status(const BedjetStatusPacket *data) override; + void on_bedjet_state(bool is_ready) override{}; + std::string describe() override; + + fan::FanTraits get_traits() override { return fan::FanTraits(false, true, false, BEDJET_FAN_SPEED_COUNT); } + + protected: + void control(const fan::FanCall &call) override; + + private: + void reset_state_(); + bool update_status_(); +}; + +} // namespace bedjet +} // namespace esphome + +#endif diff --git a/tests/test1.yaml b/tests/test1.yaml index fcc7107bd6..b741f3e255 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2219,6 +2219,9 @@ fan: on_speed_set: then: - logger.log: "Fan speed was changed!" + - platform: bedjet + name: My Bedjet fan + bedjet_id: my_bedjet_client - platform: copy source_id: fan_speed name: "Fan Speed Copy" From 15eb9605a80133035bfdeba04e86bb9f666db4b6 Mon Sep 17 00:00:00 2001 From: Wouter van der Wal <33957974+wjtje@users.noreply.github.com> Date: Wed, 31 Aug 2022 06:42:48 +0200 Subject: [PATCH 34/66] ibeacon support for the ble_rssi sensor (#3745) --- esphome/components/ble_rssi/ble_rssi_sensor.h | 77 +++++++++++++++---- esphome/components/ble_rssi/sensor.py | 28 ++++++- tests/test2.yaml | 3 + 3 files changed, 92 insertions(+), 16 deletions(-) diff --git a/esphome/components/ble_rssi/ble_rssi_sensor.h b/esphome/components/ble_rssi/ble_rssi_sensor.h index c6acae2593..0cb511de3b 100644 --- a/esphome/components/ble_rssi/ble_rssi_sensor.h +++ b/esphome/components/ble_rssi/ble_rssi_sensor.h @@ -12,41 +12,78 @@ namespace ble_rssi { class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component { public: void set_address(uint64_t address) { - this->by_address_ = true; + this->match_by_ = MATCH_BY_MAC_ADDRESS; this->address_ = address; } void set_service_uuid16(uint16_t uuid) { - this->by_address_ = false; + this->match_by_ = MATCH_BY_SERVICE_UUID; this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint16(uuid); } void set_service_uuid32(uint32_t uuid) { - this->by_address_ = false; + this->match_by_ = MATCH_BY_SERVICE_UUID; this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint32(uuid); } void set_service_uuid128(uint8_t *uuid) { - this->by_address_ = false; + this->match_by_ = MATCH_BY_SERVICE_UUID; this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(uuid); } + void set_ibeacon_uuid(uint8_t *uuid) { + this->match_by_ = MATCH_BY_IBEACON_UUID; + this->ibeacon_uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(uuid); + } + void set_ibeacon_major(uint16_t major) { + this->check_ibeacon_major_ = true; + this->ibeacon_major_ = major; + } + void set_ibeacon_minor(uint16_t minor) { + this->check_ibeacon_minor_ = true; + this->ibeacon_minor_ = minor; + } void on_scan_end() override { if (!this->found_) this->publish_state(NAN); this->found_ = false; } bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override { - if (this->by_address_) { - if (device.address_uint64() == this->address_) { - this->publish_state(device.get_rssi()); - this->found_ = true; - return true; - } - } else { - for (auto uuid : device.get_service_uuids()) { - if (this->uuid_ == uuid) { + switch (this->match_by_) { + case MATCH_BY_MAC_ADDRESS: + if (device.address_uint64() == this->address_) { this->publish_state(device.get_rssi()); this->found_ = true; return true; } - } + break; + case MATCH_BY_SERVICE_UUID: + for (auto uuid : device.get_service_uuids()) { + if (this->uuid_ == uuid) { + this->publish_state(device.get_rssi()); + this->found_ = true; + return true; + } + } + break; + case MATCH_BY_IBEACON_UUID: + if (!device.get_ibeacon().has_value()) { + return false; + } + + auto ibeacon = device.get_ibeacon().value(); + + if (this->ibeacon_uuid_ != ibeacon.get_uuid()) { + return false; + } + + if (this->check_ibeacon_major_ && this->ibeacon_major_ != ibeacon.get_major()) { + return false; + } + + if (this->check_ibeacon_minor_ && this->ibeacon_minor_ != ibeacon.get_minor()) { + return false; + } + + this->publish_state(device.get_rssi()); + this->found_ = true; + return true; } return false; } @@ -54,10 +91,20 @@ class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDevi float get_setup_priority() const override { return setup_priority::DATA; } protected: + enum MatchType { MATCH_BY_MAC_ADDRESS, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID }; + MatchType match_by_; + bool found_{false}; - bool by_address_{false}; + uint64_t address_; + esp32_ble_tracker::ESPBTUUID uuid_; + + esp32_ble_tracker::ESPBTUUID ibeacon_uuid_; + uint16_t ibeacon_major_; + bool check_ibeacon_major_; + uint16_t ibeacon_minor_; + bool check_ibeacon_minor_; }; } // namespace ble_rssi diff --git a/esphome/components/ble_rssi/sensor.py b/esphome/components/ble_rssi/sensor.py index fd2c2e5cb1..bd8e4f98e3 100644 --- a/esphome/components/ble_rssi/sensor.py +++ b/esphome/components/ble_rssi/sensor.py @@ -2,6 +2,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker from esphome.const import ( + CONF_IBEACON_MAJOR, + CONF_IBEACON_MINOR, + CONF_IBEACON_UUID, CONF_SERVICE_UUID, CONF_MAC_ADDRESS, DEVICE_CLASS_SIGNAL_STRENGTH, @@ -16,6 +19,15 @@ BLERSSISensor = ble_rssi_ns.class_( "BLERSSISensor", sensor.Sensor, cg.Component, esp32_ble_tracker.ESPBTDeviceListener ) + +def _validate(config): + if CONF_IBEACON_MAJOR in config and CONF_IBEACON_UUID not in config: + raise cv.Invalid("iBeacon major identifier requires iBeacon UUID") + if CONF_IBEACON_MINOR in config and CONF_IBEACON_UUID not in config: + raise cv.Invalid("iBeacon minor identifier requires iBeacon UUID") + return config + + CONFIG_SCHEMA = cv.All( sensor.sensor_schema( BLERSSISensor, @@ -28,11 +40,15 @@ CONFIG_SCHEMA = cv.All( { cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, + cv.Optional(CONF_IBEACON_MAJOR): cv.uint16_t, + cv.Optional(CONF_IBEACON_MINOR): cv.uint16_t, + cv.Optional(CONF_IBEACON_UUID): cv.uuid, } ) .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) .extend(cv.COMPONENT_SCHEMA), - cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID), + cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID, CONF_IBEACON_UUID), + _validate, ) @@ -60,3 +76,13 @@ async def to_code(config): elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format): uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID]) cg.add(var.set_service_uuid128(uuid128)) + + if CONF_IBEACON_UUID in config: + ibeacon_uuid = esp32_ble_tracker.as_hex_array(str(config[CONF_IBEACON_UUID])) + cg.add(var.set_ibeacon_uuid(ibeacon_uuid)) + + if CONF_IBEACON_MAJOR in config: + cg.add(var.set_ibeacon_major(config[CONF_IBEACON_MAJOR])) + + if CONF_IBEACON_MINOR in config: + cg.add(var.set_ibeacon_minor(config[CONF_IBEACON_MINOR])) diff --git a/tests/test2.yaml b/tests/test2.yaml index 110e8e6625..3dd8f824dd 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -96,6 +96,9 @@ sensor: - platform: ble_rssi service_uuid: '11223344-5566-7788-99aa-bbccddeeff00' name: 'BLE Test Service 128' + - platform: ble_rssi + service_uuid: '11223344-5566-7788-99aa-bbccddeeff00' + name: 'BLE Test iBeacon UUID' - platform: b_parasite mac_address: F0:CA:F0:CA:01:01 humidity: From 0907de8662c9657a604d26d8b693f84073dcb801 Mon Sep 17 00:00:00 2001 From: yuhei mukoyama Date: Wed, 31 Aug 2022 13:43:33 +0900 Subject: [PATCH 35/66] Add AEHA IR Protocol (#3726) --- esphome/components/remote_base/__init__.py | 45 ++++++++ .../components/remote_base/aeha_protocol.cpp | 103 ++++++++++++++++++ .../components/remote_base/aeha_protocol.h | 42 +++++++ tests/test1.yaml | 7 ++ 4 files changed, 197 insertions(+) create mode 100644 esphome/components/remote_base/aeha_protocol.cpp create mode 100644 esphome/components/remote_base/aeha_protocol.h diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 5730cba1eb..e4d1e115e7 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -1338,3 +1338,48 @@ def midea_dumper(var, config): ) async def midea_action(var, config, args): cg.add(var.set_code(config[CONF_CODE])) + + +# AEHA +AEHAData, AEHABinarySensor, AEHATrigger, AEHAAction, AEHADumper = declare_protocol( + "AEHA" +) +AEHA_SCHEMA = cv.Schema( + { + cv.Required(CONF_ADDRESS): cv.hex_uint16_t, + cv.Required(CONF_DATA): cv.All( + [cv.Any(cv.hex_uint8_t, cv.uint8_t)], + cv.Length(min=2, max=35), + ), + } +) + + +@register_binary_sensor("aeha", AEHABinarySensor, AEHA_SCHEMA) +def aeha_binary_sensor(var, config): + cg.add( + var.set_data( + cg.StructInitializer( + AEHAData, + ("address", config[CONF_ADDRESS]), + ("data", config[CONF_DATA]), + ) + ) + ) + + +@register_trigger("aeha", AEHATrigger, AEHAData) +def aeha_trigger(var, config): + pass + + +@register_dumper("aeha", AEHADumper) +def aeha_dumper(var, config): + pass + + +@register_action("aeha", AEHAAction, AEHA_SCHEMA) +async def aeha_action(var, config, args): + template_ = await cg.templatable(config[CONF_ADDRESS], args, cg.uint16) + cg.add(var.set_address(template_)) + cg.add(var.set_data(config[CONF_DATA])) diff --git a/esphome/components/remote_base/aeha_protocol.cpp b/esphome/components/remote_base/aeha_protocol.cpp new file mode 100644 index 0000000000..ee1616ed6d --- /dev/null +++ b/esphome/components/remote_base/aeha_protocol.cpp @@ -0,0 +1,103 @@ +#include "aeha_protocol.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace remote_base { + +static const char *const TAG = "remote.aeha"; + +static const uint16_t BITWISE = 425; +static const uint16_t HEADER_HIGH_US = BITWISE * 8; +static const uint16_t HEADER_LOW_US = BITWISE * 4; +static const uint16_t BIT_HIGH_US = BITWISE; +static const uint16_t BIT_ONE_LOW_US = BITWISE * 3; +static const uint16_t BIT_ZERO_LOW_US = BITWISE; +static const uint16_t TRAILER = BITWISE; + +void AEHAProtocol::encode(RemoteTransmitData *dst, const AEHAData &data) { + dst->set_carrier_frequency(38000); + dst->reserve(2 + 32 + (data.data.size() * 2) + 1); + + dst->item(HEADER_HIGH_US, HEADER_LOW_US); + + for (uint16_t mask = 1 << 15; mask != 0; mask >>= 1) { + if (data.address & mask) { + dst->item(BIT_HIGH_US, BIT_ONE_LOW_US); + } else { + dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US); + } + } + + for (uint8_t bit : data.data) { + for (uint8_t mask = 1 << 7; mask != 0; mask >>= 1) { + if (bit & mask) { + dst->item(BIT_HIGH_US, BIT_ONE_LOW_US); + } else { + dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US); + } + } + } + + dst->mark(TRAILER); +} +optional AEHAProtocol::decode(RemoteReceiveData src) { + AEHAData out{ + .address = 0, + .data = {}, + }; + if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US)) + return {}; + + for (uint16_t mask = 1 << 15; mask != 0; mask >>= 1) { + if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { + out.address |= mask; + } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + out.address &= ~mask; + } else { + return {}; + } + } + + for (uint8_t pos = 0; pos < 35; pos++) { + uint8_t data = 0; + for (uint8_t mask = 1 << 7; mask != 0; mask >>= 1) { + if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { + data |= mask; + } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + data &= ~mask; + } else if (pos > 1 && src.expect_mark(TRAILER)) { + return out; + } else { + return {}; + } + } + + out.data.push_back(data); + } + + if (src.expect_mark(TRAILER)) { + return out; + } + + return {}; +} + +std::string AEHAProtocol::format_data_(const std::vector &data) { + std::string out; + for (uint8_t byte : data) { + char buf[6]; + sprintf(buf, "0x%02X,", byte); + out += buf; + } + out.pop_back(); + return out; +} + +void AEHAProtocol::dump(const AEHAData &data) { + auto data_str = format_data_(data.data); + ESP_LOGD(TAG, "Received AEHA: address=0x%04X, data=[%s]", data.address, data_str.c_str()); +} + +} // namespace remote_base +} // namespace esphome diff --git a/esphome/components/remote_base/aeha_protocol.h b/esphome/components/remote_base/aeha_protocol.h new file mode 100644 index 0000000000..6cb4706506 --- /dev/null +++ b/esphome/components/remote_base/aeha_protocol.h @@ -0,0 +1,42 @@ +#pragma once + +#include "remote_base.h" + +namespace esphome { +namespace remote_base { + +struct AEHAData { + uint16_t address; + std::vector data; + + bool operator==(const AEHAData &rhs) const { return address == rhs.address && data == rhs.data; } +}; + +class AEHAProtocol : public RemoteProtocol { + public: + void encode(RemoteTransmitData *dst, const AEHAData &data) override; + optional decode(RemoteReceiveData src) override; + void dump(const AEHAData &data) override; + + private: + std::string format_data_(const std::vector &data); +}; + +DECLARE_REMOTE_PROTOCOL(AEHA) + +template class AEHAAction : public RemoteTransmitterActionBase { + public: + TEMPLATABLE_VALUE(uint16_t, address) + TEMPLATABLE_VALUE(std::vector, data) + + void set_data(const std::vector &data) { data_ = data; } + void encode(RemoteTransmitData *dst, Ts... x) override { + AEHAData data{}; + data.address = this->address_.value(x...); + data.data = this->data_.value(x...); + AEHAProtocol().encode(dst, data); + } +}; + +} // namespace remote_base +} // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index b741f3e255..1a9d69eac8 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2085,6 +2085,13 @@ switch: turn_on_action: remote_transmitter.transmit_raw: code: [1000, -1000] + - platform: template + name: AEHA + id: eaha_hitachi_climate_power_on + turn_on_action: + remote_transmitter.transmit_aeha: + address: 0x8008 + data: [0x00, 0x02, 0xFD, 0xFF, 0x00, 0x33, 0xCC, 0x49, 0xB6, 0xC8, 0x37, 0x16, 0xE9, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xCA, 0x35, 0x8F, 0x70, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF] - platform: template name: Living Room Lights id: livingroom_lights From acd55b960120265a0a4ce0bd06d08758dce5bbbd Mon Sep 17 00:00:00 2001 From: Fabian Berthold Date: Wed, 31 Aug 2022 06:52:16 +0200 Subject: [PATCH 36/66] Add sum type to binary_sensor_map (#3541) --- .../binary_sensor_map/binary_sensor_map.cpp | 31 +++++++++++++++++++ .../binary_sensor_map/binary_sensor_map.h | 3 ++ .../components/binary_sensor_map/sensor.py | 13 ++++++++ esphome/const.py | 1 + 4 files changed, 48 insertions(+) diff --git a/esphome/components/binary_sensor_map/binary_sensor_map.cpp b/esphome/components/binary_sensor_map/binary_sensor_map.cpp index d1123ddff0..b2dffaf74d 100644 --- a/esphome/components/binary_sensor_map/binary_sensor_map.cpp +++ b/esphome/components/binary_sensor_map/binary_sensor_map.cpp @@ -13,6 +13,9 @@ void BinarySensorMap::loop() { case BINARY_SENSOR_MAP_TYPE_GROUP: this->process_group_(); break; + case BINARY_SENSOR_MAP_TYPE_SUM: + this->process_sum_(); + break; } } @@ -46,6 +49,34 @@ void BinarySensorMap::process_group_() { this->last_mask_ = mask; } +void BinarySensorMap::process_sum_() { + float total_current_value = 0.0; + uint64_t mask = 0x00; + // check all binary_sensors for its state. when active add its value to total_current_value. + // create a bitmask for the binary_sensor status on all channels + for (size_t i = 0; i < this->channels_.size(); i++) { + auto bs = this->channels_[i]; + if (bs.binary_sensor->state) { + total_current_value += bs.sensor_value; + mask |= 1 << i; + } + } + // check if the sensor map was touched + if (mask != 0ULL) { + // did the bit_mask change or is it a new sensor touch + if (this->last_mask_ != mask) { + float publish_value = total_current_value; + ESP_LOGD(TAG, "'%s' - Publishing %.2f", this->name_.c_str(), publish_value); + this->publish_state(publish_value); + } + } else if (this->last_mask_ != 0ULL) { + // is this a new sensor release + ESP_LOGD(TAG, "'%s' - No binary sensor active, publishing 0", this->name_.c_str()); + this->publish_state(0.0); + } + this->last_mask_ = mask; +} + void BinarySensorMap::add_channel(binary_sensor::BinarySensor *sensor, float value) { BinarySensorMapChannel sensor_channel{ .binary_sensor = sensor, diff --git a/esphome/components/binary_sensor_map/binary_sensor_map.h b/esphome/components/binary_sensor_map/binary_sensor_map.h index e99b4e18d6..a880be9623 100644 --- a/esphome/components/binary_sensor_map/binary_sensor_map.h +++ b/esphome/components/binary_sensor_map/binary_sensor_map.h @@ -9,6 +9,7 @@ namespace binary_sensor_map { enum BinarySensorMapType { BINARY_SENSOR_MAP_TYPE_GROUP, + BINARY_SENSOR_MAP_TYPE_SUM, }; struct BinarySensorMapChannel { @@ -50,8 +51,10 @@ class BinarySensorMap : public sensor::Sensor, public Component { /** * methods to process the types of binary_sensor_maps * GROUP: process_group_() just map to a value + * ADD: process_add_() adds all the values * */ void process_group_(); + void process_sum_(); }; } // namespace binary_sensor_map diff --git a/esphome/components/binary_sensor_map/sensor.py b/esphome/components/binary_sensor_map/sensor.py index 7ddf0ecf2a..025be490cd 100644 --- a/esphome/components/binary_sensor_map/sensor.py +++ b/esphome/components/binary_sensor_map/sensor.py @@ -9,6 +9,7 @@ from esphome.const import ( ICON_CHECK_CIRCLE_OUTLINE, CONF_BINARY_SENSOR, CONF_GROUP, + CONF_SUM, ) DEPENDENCIES = ["binary_sensor"] @@ -21,6 +22,7 @@ SensorMapType = binary_sensor_map_ns.enum("SensorMapType") SENSOR_MAP_TYPES = { CONF_GROUP: SensorMapType.BINARY_SENSOR_MAP_TYPE_GROUP, + CONF_SUM: SensorMapType.BINARY_SENSOR_MAP_TYPE_SUM, } entry = { @@ -41,6 +43,17 @@ CONFIG_SCHEMA = cv.typed_schema( ), } ), + CONF_SUM: sensor.sensor_schema( + BinarySensorMap, + icon=ICON_CHECK_CIRCLE_OUTLINE, + accuracy_decimals=0, + ).extend( + { + cv.Required(CONF_CHANNELS): cv.All( + cv.ensure_list(entry), cv.Length(min=1) + ), + } + ), }, lower=True, ) diff --git a/esphome/const.py b/esphome/const.py index 5d3ddcf518..8245ed26f7 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -659,6 +659,7 @@ CONF_STOP_ACTION = "stop_action" CONF_STORE_BASELINE = "store_baseline" CONF_SUBNET = "subnet" CONF_SUBSTITUTIONS = "substitutions" +CONF_SUM = "sum" CONF_SUPPLEMENTAL_COOLING_ACTION = "supplemental_cooling_action" CONF_SUPPLEMENTAL_COOLING_DELTA = "supplemental_cooling_delta" CONF_SUPPLEMENTAL_HEATING_ACTION = "supplemental_heating_action" From 71697df2b60f9ee3033c76357d8ccacd8d5b1306 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Aug 2022 16:53:36 +1200 Subject: [PATCH 37/66] Bump aioesphomeapi from 10.11.0 to 10.13.0 (#3740) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f31b1662bd..f4cb4285ab 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ platformio==6.0.2 # When updating platformio, also update Dockerfile esptool==3.3.1 click==8.1.3 esphome-dashboard==20220508.0 -aioesphomeapi==10.11.0 +aioesphomeapi==10.13.0 zeroconf==0.39.0 # esp-idf requires this, but doesn't bundle it by default From 2650441013a587911d603e3e715e63ae7b808484 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Aug 2022 17:01:36 +1200 Subject: [PATCH 38/66] Bump pylint from 2.14.5 to 2.15.0 (#3746) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/font/__init__.py | 4 ++-- esphome/components/shelly_dimmer/light.py | 2 +- esphome/dashboard/dashboard.py | 4 +++- requirements_test.txt | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index ced433dc5a..aa165ebaa5 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -146,7 +146,7 @@ def download_gfonts(value): if path.is_file(): return value try: - req = requests.get(url) + req = requests.get(url, timeout=30) req.raise_for_status() except requests.exceptions.RequestException as e: raise cv.Invalid( @@ -162,7 +162,7 @@ def download_gfonts(value): ttf_url = match.group(1) try: - req = requests.get(ttf_url) + req = requests.get(ttf_url, timeout=30) req.raise_for_status() except requests.exceptions.RequestException as e: raise cv.Invalid(f"Could not download ttf file for {name} ({ttf_url}): {e}") diff --git a/esphome/components/shelly_dimmer/light.py b/esphome/components/shelly_dimmer/light.py index 003498c090..3978d37c0b 100644 --- a/esphome/components/shelly_dimmer/light.py +++ b/esphome/components/shelly_dimmer/light.py @@ -73,7 +73,7 @@ def get_firmware(value): def dl(url): try: - req = requests.get(url) + req = requests.get(url, timeout=30) req.raise_for_status() except requests.exceptions.RequestException as e: raise cv.Invalid(f"Could not download firmware file ({url}): {e}") diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index ca2767639d..e7a17cba4c 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -823,7 +823,9 @@ class LoginHandler(BaseHandler): "password": self.get_argument("password", ""), } try: - req = requests.post("http://supervisor/auth", headers=headers, data=data) + req = requests.post( + "http://supervisor/auth", headers=headers, data=data, timeout=30 + ) if req.status_code == 200: self.set_secure_cookie("authenticated", cookie_authenticated_yes) self.redirect("/") diff --git a/requirements_test.txt b/requirements_test.txt index ed48818276..8ae7563642 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,4 +1,4 @@ -pylint==2.14.5 +pylint==2.15.0 flake8==5.0.4 black==22.6.0 # also change in .pre-commit-config.yaml when updating pyupgrade==2.37.3 # also change in .pre-commit-config.yaml when updating From 1b4156646e03dacd3dfba02e092023c7d7dd188a Mon Sep 17 00:00:00 2001 From: RoboMagus <68224306+RoboMagus@users.noreply.github.com> Date: Fri, 2 Sep 2022 03:22:34 +0200 Subject: [PATCH 39/66] Esp32 pulsecounter optional pcnt (#3691) Co-authored-by: RoboMagus <-> --- esphome/components/hlw8012/hlw8012.h | 13 +++++- .../pulse_counter/pulse_counter_sensor.cpp | 23 ++++++---- .../pulse_counter/pulse_counter_sensor.h | 45 ++++++++++++------- esphome/components/pulse_counter/sensor.py | 34 +++++++++----- 4 files changed, 79 insertions(+), 36 deletions(-) diff --git a/esphome/components/hlw8012/hlw8012.h b/esphome/components/hlw8012/hlw8012.h index 5060957cf1..adb49ffb66 100644 --- a/esphome/components/hlw8012/hlw8012.h +++ b/esphome/components/hlw8012/hlw8012.h @@ -16,8 +16,17 @@ enum HLW8012SensorModels { HLW8012_SENSOR_MODEL_BL0937 }; +#ifdef HAS_PCNT +#define USE_PCNT true +#else +#define USE_PCNT false +#endif + class HLW8012Component : public PollingComponent { public: + HLW8012Component() + : cf_store_(*pulse_counter::get_storage(USE_PCNT)), cf1_store_(*pulse_counter::get_storage(USE_PCNT)) {} + void setup() override; void dump_config() override; float get_setup_priority() const override; @@ -49,9 +58,9 @@ class HLW8012Component : public PollingComponent { uint64_t cf_total_pulses_{0}; GPIOPin *sel_pin_; InternalGPIOPin *cf_pin_; - pulse_counter::PulseCounterStorage cf_store_; + pulse_counter::PulseCounterStorageBase &cf_store_; InternalGPIOPin *cf1_pin_; - pulse_counter::PulseCounterStorage cf1_store_; + pulse_counter::PulseCounterStorageBase &cf1_store_; sensor::Sensor *voltage_sensor_{nullptr}; sensor::Sensor *current_sensor_{nullptr}; sensor::Sensor *power_sensor_{nullptr}; diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.cpp b/esphome/components/pulse_counter/pulse_counter_sensor.cpp index 002f6dcac9..1f50360fed 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.cpp +++ b/esphome/components/pulse_counter/pulse_counter_sensor.cpp @@ -8,8 +8,16 @@ static const char *const TAG = "pulse_counter"; const char *const EDGE_MODE_TO_STRING[] = {"DISABLE", "INCREMENT", "DECREMENT"}; -#ifndef HAS_PCNT -void IRAM_ATTR PulseCounterStorage::gpio_intr(PulseCounterStorage *arg) { +#ifdef HAS_PCNT +PulseCounterStorageBase *get_storage(bool hw_pcnt) { + return (hw_pcnt ? (PulseCounterStorageBase *) (new HwPulseCounterStorage) + : (PulseCounterStorageBase *) (new BasicPulseCounterStorage)); +} +#else +PulseCounterStorageBase *get_storage(bool) { return new BasicPulseCounterStorage; } +#endif + +void IRAM_ATTR BasicPulseCounterStorage::gpio_intr(BasicPulseCounterStorage *arg) { const uint32_t now = micros(); const bool discard = now - arg->last_pulse < arg->filter_us; arg->last_pulse = now; @@ -28,23 +36,22 @@ void IRAM_ATTR PulseCounterStorage::gpio_intr(PulseCounterStorage *arg) { break; } } -bool PulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { +bool BasicPulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { this->pin = pin; this->pin->setup(); this->isr_pin = this->pin->to_isr(); - this->pin->attach_interrupt(PulseCounterStorage::gpio_intr, this, gpio::INTERRUPT_ANY_EDGE); + this->pin->attach_interrupt(BasicPulseCounterStorage::gpio_intr, this, gpio::INTERRUPT_ANY_EDGE); return true; } -pulse_counter_t PulseCounterStorage::read_raw_value() { +pulse_counter_t BasicPulseCounterStorage::read_raw_value() { pulse_counter_t counter = this->counter; pulse_counter_t ret = counter - this->last_value; this->last_value = counter; return ret; } -#endif #ifdef HAS_PCNT -bool PulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { +bool HwPulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { static pcnt_unit_t next_pcnt_unit = PCNT_UNIT_0; this->pin = pin; this->pin->setup(); @@ -127,7 +134,7 @@ bool PulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { } return true; } -pulse_counter_t PulseCounterStorage::read_raw_value() { +pulse_counter_t HwPulseCounterStorage::read_raw_value() { pulse_counter_t counter; pcnt_get_counter_value(this->pcnt_unit, &counter); pulse_counter_t ret = counter - this->last_value; diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.h b/esphome/components/pulse_counter/pulse_counter_sensor.h index f81d20a646..d9be79e403 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.h +++ b/esphome/components/pulse_counter/pulse_counter_sensor.h @@ -24,31 +24,44 @@ using pulse_counter_t = int16_t; using pulse_counter_t = int32_t; #endif -struct PulseCounterStorage { - bool pulse_counter_setup(InternalGPIOPin *pin); - pulse_counter_t read_raw_value(); - - static void gpio_intr(PulseCounterStorage *arg); - -#ifndef HAS_PCNT - volatile pulse_counter_t counter{0}; - volatile uint32_t last_pulse{0}; -#endif +struct PulseCounterStorageBase { + virtual bool pulse_counter_setup(InternalGPIOPin *pin) = 0; + virtual pulse_counter_t read_raw_value() = 0; InternalGPIOPin *pin; -#ifdef HAS_PCNT - pcnt_unit_t pcnt_unit; -#else - ISRInternalGPIOPin isr_pin; -#endif PulseCounterCountMode rising_edge_mode{PULSE_COUNTER_INCREMENT}; PulseCounterCountMode falling_edge_mode{PULSE_COUNTER_DISABLE}; uint32_t filter_us{0}; pulse_counter_t last_value{0}; }; +struct BasicPulseCounterStorage : public PulseCounterStorageBase { + static void gpio_intr(BasicPulseCounterStorage *arg); + + bool pulse_counter_setup(InternalGPIOPin *pin) override; + pulse_counter_t read_raw_value() override; + + volatile pulse_counter_t counter{0}; + volatile uint32_t last_pulse{0}; + + ISRInternalGPIOPin isr_pin; +}; + +#ifdef HAS_PCNT +struct HwPulseCounterStorage : public PulseCounterStorageBase { + bool pulse_counter_setup(InternalGPIOPin *pin) override; + pulse_counter_t read_raw_value() override; + + pcnt_unit_t pcnt_unit; +}; +#endif + +PulseCounterStorageBase *get_storage(bool hw_pcnt = false); + class PulseCounterSensor : public sensor::Sensor, public PollingComponent { public: + explicit PulseCounterSensor(bool hw_pcnt = false) : storage_(*get_storage(hw_pcnt)) {} + void set_pin(InternalGPIOPin *pin) { pin_ = pin; } void set_rising_edge_mode(PulseCounterCountMode mode) { storage_.rising_edge_mode = mode; } void set_falling_edge_mode(PulseCounterCountMode mode) { storage_.falling_edge_mode = mode; } @@ -65,7 +78,7 @@ class PulseCounterSensor : public sensor::Sensor, public PollingComponent { protected: InternalGPIOPin *pin_; - PulseCounterStorage storage_; + PulseCounterStorageBase &storage_; uint32_t last_time_{0}; uint32_t current_total_{0}; sensor::Sensor *total_sensor_; diff --git a/esphome/components/pulse_counter/sensor.py b/esphome/components/pulse_counter/sensor.py index 88f53bdf77..27364a34b3 100644 --- a/esphome/components/pulse_counter/sensor.py +++ b/esphome/components/pulse_counter/sensor.py @@ -20,6 +20,8 @@ from esphome.const import ( ) from esphome.core import CORE +CONF_USE_PCNT = "use_pcnt" + pulse_counter_ns = cg.esphome_ns.namespace("pulse_counter") PulseCounterCountMode = pulse_counter_ns.enum("PulseCounterCountMode") COUNT_MODES = { @@ -40,11 +42,19 @@ SetTotalPulsesAction = pulse_counter_ns.class_( def validate_internal_filter(value): - value = cv.positive_time_period_microseconds(value) - if CORE.is_esp32: - if value.total_microseconds > 13: - raise cv.Invalid("Maximum internal filter value for ESP32 is 13us") - return value + use_pcnt = value.get(CONF_USE_PCNT) + if CORE.is_esp8266 and use_pcnt: + raise cv.Invalid( + "Using hardware PCNT is only available on ESP32", + [CONF_USE_PCNT], + ) + + if CORE.is_esp32 and use_pcnt: + if value.get(CONF_INTERNAL_FILTER).total_microseconds > 13: + raise cv.Invalid( + "Maximum internal filter value when using ESP32 hardware PCNT is 13us", + [CONF_INTERNAL_FILTER], + ) return value @@ -69,7 +79,7 @@ def validate_count_mode(value): return value -CONFIG_SCHEMA = ( +CONFIG_SCHEMA = cv.All( sensor.sensor_schema( PulseCounterSensor, unit_of_measurement=UNIT_PULSES_PER_MINUTE, @@ -95,21 +105,25 @@ CONFIG_SCHEMA = ( ), validate_count_mode, ), - cv.Optional(CONF_INTERNAL_FILTER, default="13us"): validate_internal_filter, + cv.SplitDefault(CONF_USE_PCNT, esp32=True): cv.boolean, + cv.Optional( + CONF_INTERNAL_FILTER, default="13us" + ): cv.positive_time_period_microseconds, cv.Optional(CONF_TOTAL): sensor.sensor_schema( unit_of_measurement=UNIT_PULSES, icon=ICON_PULSE, accuracy_decimals=0, state_class=STATE_CLASS_TOTAL_INCREASING, ), - } + }, ) - .extend(cv.polling_component_schema("60s")) + .extend(cv.polling_component_schema("60s")), + validate_internal_filter, ) async def to_code(config): - var = await sensor.new_sensor(config) + var = await sensor.new_sensor(config, config.get(CONF_USE_PCNT)) await cg.register_component(var, config) pin = await cg.gpio_pin_expression(config[CONF_PIN]) From e3dad7c63215bbeddf154c52379b99f6cec0749f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 13:42:04 +1200 Subject: [PATCH 40/66] Bump black from 22.6.0 to 22.8.0 (#3760) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 8ae7563642..bbd707fbb3 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,6 +1,6 @@ pylint==2.15.0 flake8==5.0.4 -black==22.6.0 # also change in .pre-commit-config.yaml when updating +black==22.8.0 # also change in .pre-commit-config.yaml when updating pyupgrade==2.37.3 # also change in .pre-commit-config.yaml when updating pre-commit From 4788a6182e295abc7ee50b30234bf6f893d81479 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Fri, 2 Sep 2022 05:46:51 +0200 Subject: [PATCH 41/66] I found some issue in the ili9341 driver (#3756) --- .../components/ili9341/ili9341_display.cpp | 92 ++++++++++++------- esphome/components/ili9341/ili9341_display.h | 12 ++- 2 files changed, 71 insertions(+), 33 deletions(-) diff --git a/esphome/components/ili9341/ili9341_display.cpp b/esphome/components/ili9341/ili9341_display.cpp index c4fa5f1b10..117de3de89 100644 --- a/esphome/components/ili9341/ili9341_display.cpp +++ b/esphome/components/ili9341/ili9341_display.cpp @@ -10,7 +10,6 @@ namespace ili9341 { static const char *const TAG = "ili9341"; void ILI9341Display::setup_pins_() { - this->init_internal_(this->get_buffer_length_()); this->dc_pin_->setup(); // OUTPUT this->dc_pin_->digital_write(false); if (this->reset_pin_ != nullptr) { @@ -28,15 +27,14 @@ void ILI9341Display::setup_pins_() { void ILI9341Display::dump_config() { LOG_DISPLAY("", "ili9341", this); - ESP_LOGCONFIG(TAG, " Width: %d, Height: %d, Rotation: %d", this->width_, this->height_, this->rotation_); LOG_PIN(" Reset Pin: ", this->reset_pin_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Busy Pin: ", this->busy_pin_); - LOG_PIN(" Backlight Pin: ", this->led_pin_); LOG_UPDATE_INTERVAL(this); } -float ILI9341Display::get_setup_priority() const { return setup_priority::PROCESSOR; } +float ILI9341Display::get_setup_priority() const { return setup_priority::HARDWARE; } + void ILI9341Display::command(uint8_t value) { this->start_command_(); this->write_byte(value); @@ -88,10 +86,19 @@ void ILI9341Display::display_() { // we will only update the changed window to the display uint16_t w = this->x_high_ - this->x_low_ + 1; uint16_t h = this->y_high_ - this->y_low_ + 1; + uint32_t start_pos = ((this->y_low_ * this->width_) + x_low_); + + // check if something was displayed + if ((this->x_high_ < this->x_low_) || (this->y_high_ < this->y_low_)) { + return; + } set_addr_window_(this->x_low_, this->y_low_, w, h); + + ESP_LOGVV("ILI9341", "Start ILI9341Display::display_(xl:%d, xh:%d, yl:%d, yh:%d, w:%d, h:%d, start_pos:%d)", + this->x_low_, this->x_high_, this->y_low_, this->y_high_, w, h, start_pos); + this->start_data_(); - uint32_t start_pos = ((this->y_low_ * this->width_) + x_low_); for (uint16_t row = 0; row < h; row++) { uint32_t pos = start_pos + (row * width_); uint32_t rem = w; @@ -101,7 +108,9 @@ void ILI9341Display::display_() { this->write_array(transfer_buffer_, 2 * sz); pos += sz; rem -= sz; + App.feed_wdt(); } + App.feed_wdt(); } this->end_data_(); @@ -121,20 +130,10 @@ void ILI9341Display::fill(Color color) { this->y_high_ = this->get_height_internal() - 1; } -void ILI9341Display::fill_internal_(Color color) { - if (color.raw_32 == Color::BLACK.raw_32) { - memset(transfer_buffer_, 0, sizeof(transfer_buffer_)); - } else { - uint8_t *dst = transfer_buffer_; - auto color565 = display::ColorUtil::color_to_565(color); +void ILI9341Display::fill_internal_(uint8_t color) { + memset(transfer_buffer_, color, sizeof(transfer_buffer_)); - while (dst < transfer_buffer_ + sizeof(transfer_buffer_)) { - *dst++ = (uint8_t)(color565 >> 8); - *dst++ = (uint8_t) color565; - } - } - - uint32_t rem = this->get_width_internal() * this->get_height_internal(); + uint32_t rem = (this->get_buffer_length_() * 2); this->set_addr_window_(0, 0, this->get_width_internal(), this->get_height_internal()); this->start_data_(); @@ -147,26 +146,58 @@ void ILI9341Display::fill_internal_(Color color) { this->end_data_(); - memset(buffer_, 0, (this->get_width_internal()) * (this->get_height_internal())); + memset(buffer_, color, this->get_buffer_length_()); +} + +void ILI9341Display::rotate_my_(uint8_t m) { + uint8_t rotation = m & 3; // can't be higher than 3 + switch (rotation) { + case 0: + m = (MADCTL_MX | MADCTL_BGR); + // _width = ILI9341_TFTWIDTH; + // _height = ILI9341_TFTHEIGHT; + break; + case 1: + m = (MADCTL_MV | MADCTL_BGR); + // _width = ILI9341_TFTHEIGHT; + // _height = ILI9341_TFTWIDTH; + break; + case 2: + m = (MADCTL_MY | MADCTL_BGR); + // _width = ILI9341_TFTWIDTH; + // _height = ILI9341_TFTHEIGHT; + break; + case 3: + m = (MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR); + // _width = ILI9341_TFTHEIGHT; + // _height = ILI9341_TFTWIDTH; + break; + } + + this->command(ILI9341_MADCTL); + this->data(m); } void HOT ILI9341Display::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) return; - // low and high watermark may speed up drawing from buffer - this->x_low_ = (x < this->x_low_) ? x : this->x_low_; - this->y_low_ = (y < this->y_low_) ? y : this->y_low_; - this->x_high_ = (x > this->x_high_) ? x : this->x_high_; - this->y_high_ = (y > this->y_high_) ? y : this->y_high_; - uint32_t pos = (y * width_) + x; + uint8_t new_color; + if (this->buffer_color_mode_ == BITS_8) { - uint8_t color332 = display::ColorUtil::color_to_332(color, display::ColorOrder::COLOR_ORDER_RGB); - buffer_[pos] = color332; + new_color = display::ColorUtil::color_to_332(color, display::ColorOrder::COLOR_ORDER_RGB); } else { // if (this->buffer_color_mode_ == BITS_8_INDEXED) { - uint8_t index = display::ColorUtil::color_to_index8_palette888(color, this->palette_); - buffer_[pos] = index; + new_color = display::ColorUtil::color_to_index8_palette888(color, this->palette_); + } + + if (buffer_[pos] != new_color) { + buffer_[pos] = new_color; + // low and high watermark may speed up drawing from buffer + this->x_low_ = (x < this->x_low_) ? x : this->x_low_; + this->y_low_ = (y < this->y_low_) ? y : this->y_low_; + this->x_high_ = (x > this->x_high_) ? x : this->x_high_; + this->y_high_ = (y > this->y_high_) ? y : this->y_high_; } } @@ -252,7 +283,6 @@ void ILI9341M5Stack::initialize() { this->width_ = 320; this->height_ = 240; this->invert_display_(true); - this->fill_internal_(Color::BLACK); } // 24_TFT display @@ -260,7 +290,6 @@ void ILI9341TFT24::initialize() { this->init_lcd_(INITCMD_TFT); this->width_ = 240; this->height_ = 320; - this->fill_internal_(Color::BLACK); } // 24_TFT rotated display @@ -268,7 +297,6 @@ void ILI9341TFT24R::initialize() { this->init_lcd_(INITCMD_TFT); this->width_ = 320; this->height_ = 240; - this->fill_internal_(Color::BLACK); } } // namespace ili9341 diff --git a/esphome/components/ili9341/ili9341_display.h b/esphome/components/ili9341/ili9341_display.h index 3fbc144ac2..547c608ae8 100644 --- a/esphome/components/ili9341/ili9341_display.h +++ b/esphome/components/ili9341/ili9341_display.h @@ -5,6 +5,7 @@ #include "esphome/components/display/display_buffer.h" #include "ili9341_defines.h" #include "ili9341_init.h" +#include "esphome/core/log.h" namespace esphome { namespace ili9341 { @@ -47,6 +48,14 @@ class ILI9341Display : public PollingComponent, void setup() override { this->setup_pins_(); this->initialize(); + + this->x_low_ = this->width_; + this->y_low_ = this->height_; + this->x_high_ = 0; + this->y_high_ = 0; + + this->init_internal_(this->get_buffer_length_()); + this->fill_internal_(0x00); } display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; } @@ -59,8 +68,9 @@ class ILI9341Display : public PollingComponent, void set_addr_window_(uint16_t x, uint16_t y, uint16_t w, uint16_t h); void invert_display_(bool invert); void reset_(); - void fill_internal_(Color color); + void fill_internal_(uint8_t color); void display_(); + void rotate_my_(uint8_t m); ILI9341Model model_; int16_t width_{320}; ///< Display width as modified by current rotation From 7a4cf13e0c998bb08f85c57461d270c399dfb694 Mon Sep 17 00:00:00 2001 From: anatoly-savchenkov <48646998+anatoly-savchenkov@users.noreply.github.com> Date: Sun, 4 Sep 2022 10:21:17 +0300 Subject: [PATCH 42/66] Ignore NaN states in the integration component (#3767) --- esphome/components/integration/integration_sensor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/integration/integration_sensor.cpp b/esphome/components/integration/integration_sensor.cpp index 65fa42dd0d..2ac7caca21 100644 --- a/esphome/components/integration/integration_sensor.cpp +++ b/esphome/components/integration/integration_sensor.cpp @@ -23,6 +23,8 @@ void IntegrationSensor::setup() { } void IntegrationSensor::dump_config() { LOG_SENSOR("", "Integration Sensor", this); } void IntegrationSensor::process_sensor_value_(float value) { + if (std::isnan(value)) + return; const uint32_t now = millis(); const double old_value = this->last_value_; const double new_value = value; From 7097b7677ea8196f2e3052debe193b7c95ce0a09 Mon Sep 17 00:00:00 2001 From: Andrey Yantsen Date: Sun, 4 Sep 2022 09:23:54 +0100 Subject: [PATCH 43/66] Add the same docker tags as used in HA (#3752) --- docker/build.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docker/build.py b/docker/build.py index d5926ae3d4..ae977f87c1 100755 --- a/docker/build.py +++ b/docker/build.py @@ -88,10 +88,12 @@ def main(): sys.exit(1) # detect channel from tag - match = re.match(r'^\d+\.\d+(?:\.\d+)?(b\d+)?$', args.tag) + match = re.match(r"^(\d+\.\d+)(?:\.\d+)?(b\d+)?$", args.tag) + major_minor_version = None if match is None: channel = CHANNEL_DEV - elif match.group(1) is None: + elif match.group(2) is None: + major_minor_version = match.group(1) channel = CHANNEL_RELEASE else: channel = CHANNEL_BETA @@ -106,6 +108,11 @@ def main(): tags_to_push.append("beta") tags_to_push.append("latest") + # Compatibility with HA tags + if major_minor_version: + tags_to_push.append("stable") + tags_to_push.append(major_minor_version) + if args.command == "build": # 1. pull cache image params = DockerParams.for_type_arch(args.build_type, args.arch) From 5d712c73ea77bbf61e5abdd690da610b742dfdfa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 4 Sep 2022 20:24:26 +1200 Subject: [PATCH 44/66] Bump pytest from 7.1.1 to 7.1.3 (#3766) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index bbd707fbb3..1d57fef954 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -5,7 +5,7 @@ pyupgrade==2.37.3 # also change in .pre-commit-config.yaml when updating pre-commit # Unit tests -pytest==7.1.1 +pytest==7.1.3 pytest-cov==3.0.0 pytest-mock==3.8.2 pytest-asyncio==0.19.0 From 219c5953f152e375469ba81e4728dcbfcf9c1526 Mon Sep 17 00:00:00 2001 From: Avirsaam <107193364+Avirsaam@users.noreply.github.com> Date: Mon, 5 Sep 2022 03:50:27 +0300 Subject: [PATCH 45/66] Update modbus_controller.cpp (#3768) --- esphome/components/modbus_controller/modbus_controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index bd2fec3ece..e60b016a17 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -236,7 +236,7 @@ size_t ModbusController::create_register_ranges_() { } } - if (curr->start_address == r.start_address) { + if (curr->start_address == r.start_address && curr->register_type == r.register_type) { // use the lowest non zero value for the whole range // Because zero is the default value for skip_updates it is excluded from getting the min value. if (curr->skip_updates != 0) { From 614eb81ad7b51b86b8d12714517bb59e7ca085f1 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 6 Sep 2022 11:54:32 +1200 Subject: [PATCH 46/66] Remove unneeded line (spi component adds it) (#3778) --- esphome/components/mcp3008/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/esphome/components/mcp3008/__init__.py b/esphome/components/mcp3008/__init__.py index 431963acfd..24a48664c1 100644 --- a/esphome/components/mcp3008/__init__.py +++ b/esphome/components/mcp3008/__init__.py @@ -2,7 +2,6 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import spi from esphome.const import CONF_ID -from esphome.core import CORE DEPENDENCIES = ["spi"] AUTO_LOAD = ["sensor"] @@ -24,6 +23,3 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) await spi.register_spi_device(var, config) - - if CORE.is_esp32: - cg.add_library("SPI", None) From c317422ed76089fe0900b2e80bf676ee63ddf70f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 6 Sep 2022 12:57:21 +1200 Subject: [PATCH 47/66] Move crc16 to helpers (#3780) --- esphome/components/am2320/am2320.cpp | 24 +++--------------------- esphome/components/modbus/modbus.cpp | 16 ---------------- esphome/components/modbus/modbus.h | 2 -- esphome/components/senseair/senseair.cpp | 20 ++------------------ esphome/components/senseair/senseair.h | 1 - esphome/core/helpers.cpp | 15 +++++++++++++++ esphome/core/helpers.h | 3 +++ 7 files changed, 23 insertions(+), 58 deletions(-) diff --git a/esphome/components/am2320/am2320.cpp b/esphome/components/am2320/am2320.cpp index 8ab48a348e..ec501f2d36 100644 --- a/esphome/components/am2320/am2320.cpp +++ b/esphome/components/am2320/am2320.cpp @@ -4,33 +4,15 @@ // - Arduino - AM2320: https://github.com/EngDial/AM2320/blob/master/src/AM2320.cpp #include "am2320.h" -#include "esphome/core/log.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" namespace esphome { namespace am2320 { static const char *const TAG = "am2320"; -// ---=== Calc CRC16 ===--- -uint16_t crc_16(uint8_t *ptr, uint8_t length) { - uint16_t crc = 0xFFFF; - uint8_t i; - //------------------------------ - while (length--) { - crc ^= *ptr++; - for (i = 0; i < 8; i++) { - if ((crc & 0x01) != 0) { - crc >>= 1; - crc ^= 0xA001; - } else { - crc >>= 1; - } - } - } - return crc; -} - void AM2320Component::update() { uint8_t data[8]; data[0] = 0; @@ -98,7 +80,7 @@ bool AM2320Component::read_data_(uint8_t *data) { checksum = data[7] << 8; checksum += data[6]; - if (crc_16(data, 6) != checksum) { + if (crc16(data, 6) != checksum) { ESP_LOGW(TAG, "AM2320 Checksum invalid!"); return false; } diff --git a/esphome/components/modbus/modbus.cpp b/esphome/components/modbus/modbus.cpp index 845fa92e95..4d75675d0f 100644 --- a/esphome/components/modbus/modbus.cpp +++ b/esphome/components/modbus/modbus.cpp @@ -35,22 +35,6 @@ void Modbus::loop() { } } -uint16_t crc16(const uint8_t *data, uint8_t len) { - uint16_t crc = 0xFFFF; - while (len--) { - crc ^= *data++; - for (uint8_t i = 0; i < 8; i++) { - if ((crc & 0x01) != 0) { - crc >>= 1; - crc ^= 0xA001; - } else { - crc >>= 1; - } - } - } - return crc; -} - bool Modbus::parse_modbus_byte_(uint8_t byte) { size_t at = this->rx_buffer_.size(); this->rx_buffer_.push_back(byte); diff --git a/esphome/components/modbus/modbus.h b/esphome/components/modbus/modbus.h index 400e29e08b..629ab6dcce 100644 --- a/esphome/components/modbus/modbus.h +++ b/esphome/components/modbus/modbus.h @@ -40,8 +40,6 @@ class Modbus : public uart::UARTDevice, public Component { std::vector devices_; }; -uint16_t crc16(const uint8_t *data, uint8_t len); - class ModbusDevice { public: void set_parent(Modbus *parent) { parent_ = parent; } diff --git a/esphome/components/senseair/senseair.cpp b/esphome/components/senseair/senseair.cpp index 50b9e01f17..7a98584201 100644 --- a/esphome/components/senseair/senseair.cpp +++ b/esphome/components/senseair/senseair.cpp @@ -1,4 +1,5 @@ #include "senseair.h" +#include "esphome/core/helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -42,7 +43,7 @@ void SenseAirComponent::update() { return; } - uint16_t calc_checksum = this->senseair_checksum_(response, 11); + uint16_t calc_checksum = crc16(response, 11); uint16_t resp_checksum = (uint16_t(response[12]) << 8) | response[11]; if (resp_checksum != calc_checksum) { ESP_LOGW(TAG, "SenseAir checksum doesn't match: 0x%02X!=0x%02X", resp_checksum, calc_checksum); @@ -60,23 +61,6 @@ void SenseAirComponent::update() { this->co2_sensor_->publish_state(ppm); } -uint16_t SenseAirComponent::senseair_checksum_(uint8_t *ptr, uint8_t length) { - uint16_t crc = 0xFFFF; - uint8_t i; - while (length--) { - crc ^= *ptr++; - for (i = 0; i < 8; i++) { - if ((crc & 0x01) != 0) { - crc >>= 1; - crc ^= 0xA001; - } else { - crc >>= 1; - } - } - } - return crc; -} - void SenseAirComponent::background_calibration() { // Responses are just echoes but must be read to clear the buffer ESP_LOGD(TAG, "SenseAir Starting background calibration"); diff --git a/esphome/components/senseair/senseair.h b/esphome/components/senseair/senseair.h index c03a0848e9..bcec638f79 100644 --- a/esphome/components/senseair/senseair.h +++ b/esphome/components/senseair/senseair.h @@ -23,7 +23,6 @@ class SenseAirComponent : public PollingComponent, public uart::UARTDevice { void abc_disable(); protected: - uint16_t senseair_checksum_(uint8_t *ptr, uint8_t length); bool senseair_write_command_(const uint8_t *command, uint8_t *response, uint8_t response_length); sensor::Sensor *co2_sensor_{nullptr}; diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index d82e452c3d..3aca944a36 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -62,6 +62,21 @@ uint8_t crc8(uint8_t *data, uint8_t len) { } return crc; } +uint16_t crc16(const uint8_t *data, uint8_t len) { + uint16_t crc = 0xFFFF; + while (len--) { + crc ^= *data++; + for (uint8_t i = 0; i < 8; i++) { + if ((crc & 0x01) != 0) { + crc >>= 1; + crc ^= 0xA001; + } else { + crc >>= 1; + } + } + } + return crc; +} uint32_t fnv1_hash(const std::string &str) { uint32_t hash = 2166136261UL; for (char c : str) { diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 6bed743010..1459f0ef55 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -149,6 +149,9 @@ template T remap(U value, U min, U max, T min_out, T max /// Calculate a CRC-8 checksum of \p data with size \p len. uint8_t crc8(uint8_t *data, uint8_t len); +/// Calculate a CRC-16 checksum of \p data with size \p len. +uint16_t crc16(const uint8_t *data, uint8_t len); + /// Calculate a FNV-1 hash of \p str. uint32_t fnv1_hash(const std::string &str); From 89fd3672970ecbcaf2bcc63c5bd43936f749ed18 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 6 Sep 2022 15:48:01 +1200 Subject: [PATCH 48/66] YAML linting (#3779) --- .editorconfig | 3 +- .github/FUNDING.yml | 1 + .github/ISSUE_TEMPLATE/config.yml | 8 +- .github/dependabot.yml | 7 +- .github/workflows/ci-docker.yml | 56 +- .github/workflows/ci.yml | 21 +- .github/workflows/lock.yml | 4 +- .github/workflows/release.yml | 104 +-- .github/workflows/stale.yml | 7 +- .gitpod.yml | 10 +- .pre-commit-config.yaml | 11 +- .yamllint | 3 + .../binary_sensor/test_binary_sensor.yaml | 5 +- tests/component_tests/button/test_button.yaml | 1 + .../deep_sleep/test_deep_sleep1.yaml | 1 + .../deep_sleep/test_deep_sleep2.yaml | 1 + tests/component_tests/sensor/test_sensor.yaml | 5 +- tests/test1.yaml | 775 +++++++++--------- tests/test2.yaml | 321 ++++---- tests/test3.yaml | 427 +++++----- tests/test4.yaml | 95 +-- tests/test5.yaml | 91 +- .../test_packages/test_packages_package1.yaml | 1 + .../test_packages_package_wifi.yaml | 5 +- tests/test_packages/test_uptime_sensor.yaml | 1 + .../fixtures/yaml_util/includes/scalar.yaml | 1 + .../fixtures/yaml_util/includetest.yaml | 7 +- 27 files changed, 1039 insertions(+), 933 deletions(-) create mode 100644 .yamllint diff --git a/.editorconfig b/.editorconfig index 8ccf1eeebc..9e203f60e4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -25,10 +25,9 @@ indent_size = 2 [*.{yaml,yml}] indent_style = space indent_size = 2 -quote_type = single +quote_type = double # JSON [*.json] indent_style = space indent_size = 2 - diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 864586fe6b..a8ca63d158 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,4 @@ +--- # These are supported funding model platforms custom: https://www.nabucasa.com diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 7f99701e39..804dad47c7 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,3 +1,4 @@ +--- blank_issues_enabled: false contact_links: - name: Issue Tracker @@ -5,7 +6,10 @@ contact_links: about: Please create bug reports in the dedicated issue tracker. - name: Feature Request Tracker url: https://github.com/esphome/feature-requests - about: Please create feature requests in the dedicated feature request tracker. + about: | + Please create feature requests in the dedicated feature request tracker. - name: Frequently Asked Question url: https://esphome.io/guides/faq.html - about: Please view the FAQ for common questions and what to include in a bug report. + about: | + Please view the FAQ for common questions and what + to include in a bug report. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c67378093e..666532f360 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,13 +1,14 @@ +--- version: 2 updates: - - package-ecosystem: "pip" + - package-ecosystem: pip directory: "/" schedule: - interval: "daily" + interval: daily ignore: # Hypotehsis is only used for testing and is updated quite often - dependency-name: hypothesis - - package-ecosystem: "github-actions" + - package-ecosystem: github-actions directory: "/" schedule: interval: daily diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index d424bd3b60..7eb4cd1f66 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -1,21 +1,23 @@ +--- name: CI for docker images # Only run when docker paths change +# yamllint disable-line rule:truthy on: push: branches: [dev, beta, release] paths: - - 'docker/**' - - '.github/workflows/**' - - 'requirements*.txt' - - 'platformio.ini' + - "docker/**" + - ".github/workflows/**" + - "requirements*.txt" + - "platformio.ini" pull_request: paths: - - 'docker/**' - - '.github/workflows/**' - - 'requirements*.txt' - - 'platformio.ini' + - "docker/**" + - ".github/workflows/**" + - "requirements*.txt" + - "platformio.ini" permissions: contents: read @@ -30,24 +32,24 @@ jobs: arch: [amd64, armv7, aarch64] build_type: ["ha-addon", "docker", "lint"] steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.9' - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.9" + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 - - name: Set TAG - run: | - echo "TAG=check" >> $GITHUB_ENV + - name: Set TAG + run: | + echo "TAG=check" >> $GITHUB_ENV - - name: Run build - run: | - docker/build.py \ - --tag "${TAG}" \ - --arch "${{ matrix.arch }}" \ - --build-type "${{ matrix.build_type }}" \ - build + - name: Run build + run: | + docker/build.py \ + --tag "${TAG}" \ + --arch "${{ matrix.arch }}" \ + --build-type "${{ matrix.build_type }}" \ + build diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1705610947..7496ccf388 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,7 @@ +--- name: CI +# yamllint disable-line rule:truthy on: push: branches: [dev, beta, release] @@ -10,6 +12,7 @@ permissions: contents: read concurrency: + # yamllint disable-line rule:line-length group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true @@ -73,6 +76,8 @@ jobs: name: Run script/clang-tidy for ESP32 IDF options: --environment esp32-idf-tidy --grep USE_ESP_IDF pio_cache_key: tidyesp32-idf + - id: yamllint + name: Run yamllint steps: - uses: actions/checkout@v3 @@ -80,17 +85,19 @@ jobs: uses: actions/setup-python@v4 id: python with: - python-version: '3.8' + python-version: "3.8" - name: Cache virtualenv uses: actions/cache@v3 with: path: .venv + # yamllint disable-line rule:line-length key: venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }} restore-keys: | venv-${{ steps.python.outputs.python-version }}- - name: Set up virtualenv + # yamllint disable rule:line-length run: | python -m venv .venv source .venv/bin/activate @@ -99,12 +106,14 @@ jobs: pip install -e . echo "$GITHUB_WORKSPACE/.venv/bin" >> $GITHUB_PATH echo "VIRTUAL_ENV=$GITHUB_WORKSPACE/.venv" >> $GITHUB_ENV + # yamllint enable rule:line-length # Use per check platformio cache because checks use different parts - name: Cache platformio uses: actions/cache@v3 with: path: ~/.platformio + # yamllint disable-line rule:line-length key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }} if: matrix.id == 'test' || matrix.id == 'clang-tidy' @@ -145,8 +154,9 @@ jobs: pytest -vv --tb=native tests if: matrix.id == 'pytest' - # Also run git-diff-index so that the step is marked as failed on formatting errors, - # since clang-format doesn't do anything but change files if -i is passed. + # Also run git-diff-index so that the step is marked as failed on + # formatting errors, since clang-format doesn't do anything but + # change files if -i is passed. - name: Run clang-format run: | script/clang-format -i @@ -161,6 +171,11 @@ jobs: # Also cache libdeps, store them in a ~/.platformio subfolder PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps + - name: Run yamllint + if: matrix.id == 'yamllint' + uses: frenck/action-yamllint@v1.2.0 + - name: Suggested changes run: script/ci-suggest-changes + # yamllint disable-line rule:line-length if: always() && (matrix.id == 'clang-tidy' || matrix.id == 'clang-format' || matrix.id == 'lint-python') diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index ceb45b2a91..1cf82895f3 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -1,8 +1,10 @@ +--- name: Lock +# yamllint disable-line rule:truthy on: schedule: - - cron: '30 0 * * *' + - cron: "30 0 * * *" workflow_dispatch: permissions: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 216c094122..65d170f5d3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,5 +1,7 @@ +--- name: Publish Release +# yamllint disable-line rule:truthy on: workflow_dispatch: release: @@ -20,6 +22,7 @@ jobs: - uses: actions/checkout@v3 - name: Get tag id: tag + # yamllint disable rule:line-length run: | if [[ "$GITHUB_EVENT_NAME" = "release" ]]; then TAG="${GITHUB_REF#refs/tags/}" @@ -29,6 +32,7 @@ jobs: TAG="${TAG}${today}" fi echo "::set-output name=tag::${TAG}" + # yamllint enable rule:line-length deploy-pypi: name: Build and publish to PyPi @@ -39,7 +43,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: '3.x' + python-version: "3.x" - name: Set up python environment run: | script/setup @@ -65,37 +69,37 @@ jobs: arch: [amd64, armv7, aarch64] build_type: ["ha-addon", "docker", "lint"] steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.9' + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.9" - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 - - name: Log in to docker hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKER_USER }} - password: ${{ secrets.DOCKER_PASSWORD }} - - name: Log in to the GitHub container registry - uses: docker/login-action@v2 - with: + - name: Log in to docker hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Log in to the GitHub container registry + uses: docker/login-action@v2 + with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push - run: | - docker/build.py \ - --tag "${{ needs.init.outputs.tag }}" \ - --arch "${{ matrix.arch }}" \ - --build-type "${{ matrix.build_type }}" \ - build \ - --push + - name: Build and push + run: | + docker/build.py \ + --tag "${{ needs.init.outputs.tag }}" \ + --arch "${{ matrix.arch }}" \ + --build-type "${{ matrix.build_type }}" \ + build \ + --push deploy-docker-manifest: if: github.repository == 'esphome/esphome' @@ -108,34 +112,34 @@ jobs: matrix: build_type: ["ha-addon", "docker", "lint"] steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.9' - - name: Enable experimental manifest support - run: | - mkdir -p ~/.docker - echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.9" + - name: Enable experimental manifest support + run: | + mkdir -p ~/.docker + echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json - - name: Log in to docker hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKER_USER }} - password: ${{ secrets.DOCKER_PASSWORD }} - - name: Log in to the GitHub container registry - uses: docker/login-action@v2 - with: + - name: Log in to docker hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Log in to the GitHub container registry + uses: docker/login-action@v2 + with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Run manifest - run: | - docker/build.py \ - --tag "${{ needs.init.outputs.tag }}" \ - --build-type "${{ matrix.build_type }}" \ - manifest + - name: Run manifest + run: | + docker/build.py \ + --tag "${{ needs.init.outputs.tag }}" \ + --build-type "${{ matrix.build_type }}" \ + manifest deploy-ha-addon-repo: if: github.repository == 'esphome/esphome' && github.event_name == 'release' @@ -144,6 +148,7 @@ jobs: steps: - env: TOKEN: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }} + # yamllint disable rule:line-length run: | TAG="${GITHUB_REF#refs/tags/}" curl \ @@ -152,3 +157,4 @@ jobs: -H "Accept: application/vnd.github.v3+json" \ https://api.github.com/repos/esphome/home-assistant-addon/actions/workflows/bump-version.yml/dispatches \ -d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}" + # yamllint enable rule:line-length diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index b998043039..33f7ad041c 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,8 +1,10 @@ +--- name: Stale +# yamllint disable-line rule:truthy on: schedule: - - cron: '30 0 * * *' + - cron: "30 0 * * *" workflow_dispatch: permissions: @@ -31,7 +33,8 @@ jobs: and will be closed if no further activity occurs within 7 days. Thank you for your contributions. - # Use stale to automatically close issues with a reference to the issue tracker + # Use stale to automatically close issues with a + # reference to the issue tracker close-issues: runs-on: ubuntu-latest steps: diff --git a/.gitpod.yml b/.gitpod.yml index e3f786a403..be2f11227c 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,6 +1,8 @@ +--- ports: -- port: 6052 - onOpen: open-preview + - port: 6052 + onOpen: open-preview tasks: -- before: pyenv local $(pyenv version | grep '^3\.' | cut -d ' ' -f 1) && script/setup - command: python -m esphome dashboard config + # yamllint disable-line rule:line-length + - before: pyenv local $(pyenv version | grep '^3\.' | cut -d ' ' -f 1) && script/setup + command: python -m esphome dashboard config diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 95365ff5bb..083aea117d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,14 +1,15 @@ +--- # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/ambv/black rev: 22.6.0 hooks: - - id: black - args: - - --safe - - --quiet - files: ^((esphome|script|tests)/.+)?[^/]+\.py$ + - id: black + args: + - --safe + - --quiet + files: ^((esphome|script|tests)/.+)?[^/]+\.py$ - repo: https://gitlab.com/pycqa/flake8 rev: 4.0.1 hooks: diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000000..4fea263214 --- /dev/null +++ b/.yamllint @@ -0,0 +1,3 @@ +--- +ignore: | + venv/ diff --git a/tests/component_tests/binary_sensor/test_binary_sensor.yaml b/tests/component_tests/binary_sensor/test_binary_sensor.yaml index 912ae115eb..f98ce693f7 100644 --- a/tests/component_tests/binary_sensor/test_binary_sensor.yaml +++ b/tests/component_tests/binary_sensor/test_binary_sensor.yaml @@ -1,3 +1,4 @@ +--- esphome: name: test platform: ESP8266 @@ -6,13 +7,13 @@ esphome: binary_sensor: - platform: gpio id: bs_1 - name: "test bs1" + name: test bs1 internal: true pin: number: D0 - platform: gpio id: bs_2 - name: "test bs2" + name: test bs2 internal: false pin: number: D1 diff --git a/tests/component_tests/button/test_button.yaml b/tests/component_tests/button/test_button.yaml index 32d2e8d93b..48e13f0353 100644 --- a/tests/component_tests/button/test_button.yaml +++ b/tests/component_tests/button/test_button.yaml @@ -1,3 +1,4 @@ +--- esphome: name: test platform: ESP8266 diff --git a/tests/component_tests/deep_sleep/test_deep_sleep1.yaml b/tests/component_tests/deep_sleep/test_deep_sleep1.yaml index 18a425df58..96514a677f 100644 --- a/tests/component_tests/deep_sleep/test_deep_sleep1.yaml +++ b/tests/component_tests/deep_sleep/test_deep_sleep1.yaml @@ -1,3 +1,4 @@ +--- esphome: name: test platform: ESP32 diff --git a/tests/component_tests/deep_sleep/test_deep_sleep2.yaml b/tests/component_tests/deep_sleep/test_deep_sleep2.yaml index 49a7f510f2..0e8e598402 100644 --- a/tests/component_tests/deep_sleep/test_deep_sleep2.yaml +++ b/tests/component_tests/deep_sleep/test_deep_sleep2.yaml @@ -1,3 +1,4 @@ +--- esphome: name: test platform: ESP32 diff --git a/tests/component_tests/sensor/test_sensor.yaml b/tests/component_tests/sensor/test_sensor.yaml index a38dd14041..8c0fd85b17 100644 --- a/tests/component_tests/sensor/test_sensor.yaml +++ b/tests/component_tests/sensor/test_sensor.yaml @@ -1,3 +1,4 @@ +--- esphome: name: test platform: ESP8266 @@ -7,6 +8,6 @@ sensor: - platform: adc pin: A0 id: s_1 - name: "test s1" + name: test s1 update_interval: 60s - device_class: "voltage" + device_class: voltage diff --git a/tests/test1.yaml b/tests/test1.yaml index 1a9d69eac8..274b25548b 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1,3 +1,4 @@ +--- substitutions: devicename: test1 sensorname: my @@ -43,13 +44,13 @@ esphome: json: key: !lambda |- return id(${textname}_text).state; - greeting: "Hello World" + greeting: Hello World - http_request.send: method: PUT url: https://esphome.io headers: Content-Type: application/json - body: "Some data" + body: Some data verify_ssl: false on_response: then: @@ -95,8 +96,8 @@ mqtt: password: "debug" client_id: someclient use_abbreviations: false - discovery: True - discovery_retain: False + discovery: true + discovery_retain: false discovery_prefix: discovery discovery_unique_id_generator: legacy topic_prefix: helloworld @@ -109,7 +110,7 @@ mqtt: topic: topic/to/send/to payload: hi qos: 2 - retain: True + retain: true keepalive: 60s reboot_timeout: 60s on_message: @@ -154,6 +155,7 @@ mqtt: return effect; - light.control: id: ${roomname}_lights + # yamllint disable-line rule:line-length brightness: !lambda "return id(${roomname}_lights).current_values.get_brightness() + 0.5;" - light.dim_relative: id: ${roomname}_lights @@ -179,7 +181,7 @@ mqtt: i2c: sda: 21 scl: 22 - scan: True + scan: true frequency: 100kHz setup_priority: -100 id: i2c_bus @@ -192,10 +194,10 @@ spi: uart: - tx_pin: number: GPIO22 - inverted: yes + inverted: true rx_pin: number: GPIO23 - inverted: yes + inverted: true baud_rate: 115200 id: uart0 parity: NONE @@ -222,7 +224,7 @@ uart: rx_buffer_size: 1024 ota: - safe_mode: True + safe_mode: true password: "superlongpasswordthatnoonewillknow" port: 3286 reboot_timeout: 2min @@ -233,14 +235,14 @@ ota: ESP_LOGD("ota", "State %d", state); on_begin: then: - logger.log: "OTA begin" + logger.log: OTA begin on_progress: then: lambda: >- ESP_LOGD("ota", "Got progress %f", x); on_end: then: - logger.log: "OTA end" + logger.log: OTA end on_error: then: lambda: >- @@ -258,7 +260,7 @@ web_server: version: 2 power_supply: - id: "atx_power_supply" + id: atx_power_supply enable_time: 20ms keep_on_time: 10s pin: @@ -308,22 +310,22 @@ bedjet: id: my_bedjet_client time_id: sntp_time mcp23s08: - - id: "mcp23s08_hub" + - id: mcp23s08_hub cs_pin: GPIO12 deviceaddress: 0 mcp23s17: - - id: "mcp23s17_hub" + - id: mcp23s17_hub cs_pin: GPIO12 deviceaddress: 1 sensor: - platform: ble_client ble_client_id: ble_foo - name: "Green iTag btn" - service_uuid: "ffe0" - characteristic_uuid: "ffe1" - descriptor_uuid: "ffe2" + name: Green iTag btn + service_uuid: ffe0 + characteristic_uuid: ffe1 + descriptor_uuid: ffe2 notify: true update_interval: never lambda: |- @@ -335,7 +337,7 @@ sensor: ESP_LOGD("green_btn", "Button was pressed, val%f", x); - platform: adc pin: A0 - name: "Living Room Brightness" + name: Living Room Brightness update_interval: "1:01" attenuation: 2.5db unit_of_measurement: "°C" @@ -384,11 +386,13 @@ sensor: - lambda: return x * (9.0/5.0) + 32.0; on_value: then: + # yamllint disable rule:line-length - lambda: |- ESP_LOGD("main", "Got value %f", x); id(${sensorname}_sensor).publish_state(42.0); ESP_LOGI("main", "Value of my sensor: %f", id(${sensorname}_sensor).state); ESP_LOGI("main", "Raw Value of my sensor: %f", id(${sensorname}_sensor).state); + # yamllint enable rule:line-length on_value_range: above: 5 below: 10 @@ -405,18 +409,18 @@ sensor: ESP_LOGD("main", "Got raw value %f", x); - logger.log: level: DEBUG - format: "Got raw value %f" + format: Got raw value %f args: ["x"] - - logger.log: "Got raw value NAN" + - logger.log: Got raw value NAN - mqtt.publish: topic: some/topic payload: Hello qos: 2 - retain: True + retain: true - platform: esp32_hall name: ESP32 Hall Sensor - platform: ads1115 - multiplexer: "A0_A1" + multiplexer: A0_A1 gain: 1.024 id: ${sensorname}_sensor filters: @@ -427,57 +431,57 @@ sensor: cs_pin: 5 phase_a: voltage: - name: "EMON Line Voltage A" + name: EMON Line Voltage A current: - name: "EMON CT1 Current" + name: EMON CT1 Current power: - name: "EMON Active Power CT1" + name: EMON Active Power CT1 reactive_power: - name: "EMON Reactive Power CT1" + name: EMON Reactive Power CT1 power_factor: - name: "EMON Power Factor CT1" + name: EMON Power Factor CT1 gain_voltage: 7305 gain_ct: 27961 phase_b: current: - name: "EMON CT2 Current" + name: EMON CT2 Current power: - name: "EMON Active Power CT2" + name: EMON Active Power CT2 reactive_power: - name: "EMON Reactive Power CT2" + name: EMON Reactive Power CT2 power_factor: - name: "EMON Power Factor CT2" + name: EMON Power Factor CT2 gain_voltage: 7305 gain_ct: 27961 phase_c: current: - name: "EMON CT3 Current" + name: EMON CT3 Current power: - name: "EMON Active Power CT3" + name: EMON Active Power CT3 reactive_power: - name: "EMON Reactive Power CT3" + name: EMON Reactive Power CT3 power_factor: - name: "EMON Power Factor CT3" + name: EMON Power Factor CT3 gain_voltage: 7305 gain_ct: 27961 frequency: - name: "EMON Line Frequency" + name: EMON Line Frequency chip_temperature: - name: "EMON Chip Temp A" + name: EMON Chip Temp A line_frequency: 60Hz current_phases: 3 gain_pga: 2X - platform: bh1750 - name: "Living Room Brightness 3" + name: Living Room Brightness 3 internal: true address: 0x23 update_interval: 30s - retain: False + retain: false availability: state_topic: livingroom/custom_state_topic i2c_id: i2c_bus - platform: max44009 - name: "Outside Brightness 1" + name: Outside Brightness 1 internal: true address: 0x4A update_interval: 30s @@ -485,13 +489,13 @@ sensor: i2c_id: i2c_bus - platform: bme280 temperature: - name: "Outside Temperature" + name: Outside Temperature oversampling: 16x pressure: - name: "Outside Pressure" + name: Outside Pressure oversampling: none humidity: - name: "Outside Humidity" + name: Outside Humidity oversampling: 8x address: 0x77 iir_filter: 16x @@ -499,14 +503,14 @@ sensor: i2c_id: i2c_bus - platform: bme680 temperature: - name: "Outside Temperature" + name: Outside Temperature oversampling: 16x pressure: - name: "Outside Pressure" + name: Outside Pressure humidity: - name: "Outside Humidity" + name: Outside Humidity gas_resistance: - name: "Outside Gas Sensor" + name: Outside Gas Sensor address: 0x77 heater: temperature: 320 @@ -515,9 +519,9 @@ sensor: i2c_id: i2c_bus - platform: bmp085 temperature: - name: "Outside Temperature" + name: Outside Temperature pressure: - name: "Outside Pressure" + name: Outside Pressure filters: - lambda: >- return x / powf(1.0 - (x / 44330.0), 5.255); @@ -525,54 +529,54 @@ sensor: i2c_id: i2c_bus - platform: bmp280 temperature: - name: "Outside Temperature" + name: Outside Temperature oversampling: 16x pressure: - name: "Outside Pressure" + name: Outside Pressure address: 0x77 update_interval: 15s iir_filter: 16x i2c_id: i2c_bus - platform: dallas address: 0x1C0000031EDD2A28 - name: "Living Room Temperature" + name: Living Room Temperature resolution: 9 - platform: dallas index: 1 - name: "Living Room Temperature 2" + name: Living Room Temperature 2 - platform: dht pin: GPIO26 temperature: - name: "Living Room Temperature 3" + name: Living Room Temperature 3 humidity: - name: "Living Room Humidity 3" + name: Living Room Humidity 3 model: AM2302 update_interval: 15s - platform: dht12 temperature: - name: "Living Room Temperature 4" + name: Living Room Temperature 4 humidity: - name: "Living Room Humidity 4" + name: Living Room Humidity 4 update_interval: 15s i2c_id: i2c_bus - platform: duty_cycle pin: GPIO25 name: Duty Cycle Sensor - platform: esp32_hall - name: "ESP32 Hall Sensor" + name: ESP32 Hall Sensor update_interval: 15s - platform: ens210 temperature: - name: "Living Room Temperature 5" + name: Living Room Temperature 5 humidity: - name: 'Living Room Humidity 5' + name: Living Room Humidity 5 update_interval: 15s i2c_id: i2c_bus - platform: hdc1080 temperature: - name: 'Living Room Temperature 6' + name: Living Room Temperature 6 humidity: - name: 'Living Room Humidity 5' + name: Living Room Humidity 5 update_interval: 15s i2c_id: i2c_bus - platform: hlw8012 @@ -580,14 +584,14 @@ sensor: cf_pin: 14 cf1_pin: 13 current: - name: "HLW8012 Current" + name: HLW8012 Current voltage: - name: "HLW8012 Voltage" + name: HLW8012 Voltage power: - name: "HLW8012 Power" + name: HLW8012 Power id: hlw8012_power energy: - name: "HLW8012 Energy" + name: HLW8012 Energy id: hlw8012_energy update_interval: 15s current_resistor: 0.001 ohm @@ -597,53 +601,53 @@ sensor: model: hlw8012 - platform: total_daily_energy power_id: hlw8012_power - name: "HLW8012 Total Daily Energy" + name: HLW8012 Total Daily Energy - platform: integration sensor: hlw8012_power - name: "Integration Sensor" + name: Integration Sensor time_unit: s - platform: integration sensor: hlw8012_power - name: "Integration Sensor lazy" + name: Integration Sensor lazy time_unit: s - platform: hmc5883l address: 0x68 field_strength_x: - name: "HMC5883L Field Strength X" + name: HMC5883L Field Strength X field_strength_y: - name: "HMC5883L Field Strength Y" + name: HMC5883L Field Strength Y field_strength_z: - name: "HMC5883L Field Strength Z" + name: HMC5883L Field Strength Z heading: - name: "HMC5883L Heading" + name: HMC5883L Heading range: 130uT oversampling: 8x update_interval: 15s i2c_id: i2c_bus - platform: honeywellabp pressure: - name: "Honeywell pressure" + name: Honeywell pressure min_pressure: 0 max_pressure: 15 temperature: - name: "Honeywell temperature" + name: Honeywell temperature cs_pin: GPIO5 - platform: qmc5883l address: 0x0D field_strength_x: - name: "QMC5883L Field Strength X" + name: QMC5883L Field Strength X field_strength_y: - name: "QMC5883L Field Strength Y" + name: QMC5883L Field Strength Y field_strength_z: - name: "QMC5883L Field Strength Z" + name: QMC5883L Field Strength Z heading: - name: "QMC5883L Heading" + name: QMC5883L Heading range: 800uT oversampling: 256x update_interval: 15s i2c_id: i2c_bus - platform: hx711 - name: "HX711 Value" + name: HX711 Value dout_pin: GPIO23 clk_pin: GPIO25 gain: 128 @@ -652,13 +656,13 @@ sensor: address: 0x40 shunt_resistance: 0.1 ohm current: - name: "INA219 Current" + name: INA219 Current power: - name: "INA219 Power" + name: INA219 Power bus_voltage: - name: "INA219 Bus Voltage" + name: INA219 Bus Voltage shunt_voltage: - name: "INA219 Shunt Voltage" + name: INA219 Shunt Voltage max_voltage: 32.0V max_current: 3.2A update_interval: 15s @@ -667,13 +671,13 @@ sensor: address: 0x40 shunt_resistance: 0.1 ohm current: - name: "INA226 Current" + name: INA226 Current power: - name: "INA226 Power" + name: INA226 Power bus_voltage: - name: "INA226 Bus Voltage" + name: INA226 Bus Voltage shunt_voltage: - name: "INA226 Shunt Voltage" + name: INA226 Shunt Voltage max_current: 3.2A update_interval: 15s i2c_id: i2c_bus @@ -682,17 +686,17 @@ sensor: channel_1: shunt_resistance: 0.1 ohm current: - name: "INA3221 Channel 1 Current" + name: INA3221 Channel 1 Current power: - name: "INA3221 Channel 1 Power" + name: INA3221 Channel 1 Power bus_voltage: - name: "INA3221 Channel 1 Bus Voltage" + name: INA3221 Channel 1 Bus Voltage shunt_voltage: - name: "INA3221 Channel 1 Shunt Voltage" + name: INA3221 Channel 1 Shunt Voltage update_interval: 15s i2c_id: i2c_bus - platform: kalman_combinator - name: "Kalman-filtered temperature" + name: Kalman-filtered temperature process_std_dev: 0.00139 sources: - source: scd30_temperature @@ -702,114 +706,114 @@ sensor: error: 1.5 - platform: htu21d temperature: - name: "Living Room Temperature 6" + name: Living Room Temperature 6 humidity: - name: "Living Room Humidity 6" + name: Living Room Humidity 6 update_interval: 15s i2c_id: i2c_bus - platform: max6675 - name: "Living Room Temperature" + name: Living Room Temperature cs_pin: GPIO23 update_interval: 15s - platform: max31855 - name: "Den Temperature" + name: Den Temperature cs_pin: GPIO23 update_interval: 15s reference_temperature: - name: "MAX31855 Internal Temperature" + name: MAX31855 Internal Temperature - platform: max31856 - name: "BBQ Temperature" + name: BBQ Temperature cs_pin: GPIO17 update_interval: 15s mains_filter: 50Hz - platform: max31865 - name: "Water Tank Temperature" + name: Water Tank Temperature cs_pin: GPIO23 update_interval: 15s - reference_resistance: "430 Ω" - rtd_nominal_resistance: "100 Ω" + reference_resistance: 430 Ω + rtd_nominal_resistance: 100 Ω - platform: mhz19 uart_id: uart0 co2: - name: "MH-Z19 CO2 Value" + name: MH-Z19 CO2 Value temperature: - name: "MH-Z19 Temperature" + name: MH-Z19 Temperature update_interval: 15s automatic_baseline_calibration: false - platform: mpu6050 address: 0x68 accel_x: - name: "MPU6050 Accel X" + name: MPU6050 Accel X accel_y: - name: "MPU6050 Accel Y" + name: MPU6050 Accel Y accel_z: - name: "MPU6050 Accel z" + name: MPU6050 Accel z gyro_x: - name: "MPU6050 Gyro X" + name: MPU6050 Gyro X gyro_y: - name: "MPU6050 Gyro Y" + name: MPU6050 Gyro Y gyro_z: - name: "MPU6050 Gyro z" + name: MPU6050 Gyro z temperature: - name: "MPU6050 Temperature" + name: MPU6050 Temperature i2c_id: i2c_bus - platform: mpu6886 address: 0x68 accel_x: - name: "MPU6886 Accel X" + name: MPU6886 Accel X accel_y: - name: "MPU6886 Accel Y" + name: MPU6886 Accel Y accel_z: - name: "MPU6886 Accel z" + name: MPU6886 Accel z gyro_x: - name: "MPU6886 Gyro X" + name: MPU6886 Gyro X gyro_y: - name: "MPU6886 Gyro Y" + name: MPU6886 Gyro Y gyro_z: - name: "MPU6886 Gyro z" + name: MPU6886 Gyro z temperature: - name: "MPU6886 Temperature" + name: MPU6886 Temperature i2c_id: i2c_bus - platform: dps310 temperature: - name: "DPS310 Temperature" + name: DPS310 Temperature pressure: - name: "DPS310 Pressure" + name: DPS310 Pressure address: 0x77 update_interval: 15s i2c_id: i2c_bus - platform: ms5611 temperature: - name: "Outside Temperature" + name: Outside Temperature pressure: - name: "Outside Pressure" + name: Outside Pressure address: 0x77 update_interval: 15s i2c_id: i2c_bus - platform: pmsa003i pm_1_0: - name: "PMSA003i PM1.0" + name: PMSA003i PM1.0 pm_2_5: - name: "PMSA003i PM2.5" + name: PMSA003i PM2.5 pm_10_0: - name: "PMSA003i PM10.0" + name: PMSA003i PM10.0 pmc_0_3: - name: "PMSA003i PMC <0.3µm" + name: PMSA003i PMC <0.3µm pmc_0_5: - name: "PMSA003i PMC <0.5µm" + name: PMSA003i PMC <0.5µm pmc_1_0: - name: "PMSA003i PMC <1µm" + name: PMSA003i PMC <1µm pmc_2_5: - name: "PMSA003i PMC <2.5µm" + name: PMSA003i PMC <2.5µm pmc_5_0: - name: "PMSA003i PMC <5µm" + name: PMSA003i PMC <5µm pmc_10_0: - name: "PMSA003i PMC <10µm" + name: PMSA003i PMC <10µm address: 0x12 - standard_units: True + standard_units: true i2c_id: i2c_bus - platform: pulse_counter - name: "Pulse Counter" + name: Pulse Counter pin: GPIO12 count_mode: rising_edge: INCREMENT @@ -817,7 +821,7 @@ sensor: internal_filter: 13us update_interval: 15s - platform: pulse_meter - name: "Pulse Meter" + name: Pulse Meter id: pulse_meter_sensor pin: GPIO12 internal_filter: 100ms @@ -827,20 +831,20 @@ sensor: id: pulse_meter_sensor value: 12345 total: - name: "Pulse Meter Total" + name: Pulse Meter Total - platform: qmp6988 temperature: - name: "Living Temperature QMP" + name: Living Temperature QMP oversampling: 32x pressure: - name: "Living Pressure QMP" + name: Living Pressure QMP oversampling: 2x address: 0x70 update_interval: 30s iir_filter: 16x i2c_id: i2c_bus - platform: rotary_encoder - name: "Rotary Encoder" + name: Rotary Encoder id: rotary_encoder1 pin_a: GPIO23 pin_b: GPIO25 @@ -860,49 +864,49 @@ sensor: id: rotary_encoder1 value: !lambda "return -1;" on_clockwise: - - logger.log: "Clockwise" + - logger.log: Clockwise on_anticlockwise: - - logger.log: "Anticlockwise" + - logger.log: Anticlockwise - platform: pulse_width name: Pulse Width pin: GPIO12 - platform: sm300d2 uart_id: uart0 co2: - name: "SM300D2 CO2 Value" + name: SM300D2 CO2 Value formaldehyde: - name: "SM300D2 Formaldehyde Value" + name: SM300D2 Formaldehyde Value tvoc: - name: "SM300D2 TVOC Value" + name: SM300D2 TVOC Value pm_2_5: - name: "SM300D2 PM2.5 Value" + name: SM300D2 PM2.5 Value pm_10_0: - name: "SM300D2 PM10 Value" + name: SM300D2 PM10 Value temperature: - name: "SM300D2 Temperature Value" + name: SM300D2 Temperature Value humidity: - name: "SM300D2 Humidity Value" + name: SM300D2 Humidity Value update_interval: 60s - platform: sht3xd temperature: - name: "Living Room Temperature 8" + name: Living Room Temperature 8 humidity: - name: "Living Room Humidity 8" + name: Living Room Humidity 8 address: 0x44 i2c_id: i2c_bus update_interval: 15s - platform: sts3x - name: "Living Room Temperature 9" + name: Living Room Temperature 9 address: 0x4A i2c_id: i2c_bus - platform: scd30 co2: - name: "Living Room CO2 9" + name: Living Room CO2 9 temperature: id: scd30_temperature - name: "Living Room Temperature 9" + name: Living Room Temperature 9 humidity: - name: "Living Room Humidity 9" + name: Living Room Humidity 9 address: 0x61 update_interval: 15s automatic_self_calibration: true @@ -913,12 +917,12 @@ sensor: - platform: scd4x id: scd40 co2: - name: "SCD4X CO2" + name: SCD4X CO2 temperature: id: scd4x_temperature - name: "SCD4X Temperature" + name: SCD4X Temperature humidity: - name: "SCD4X Humidity" + name: SCD4X Humidity update_interval: 15s automatic_self_calibration: true altitude_compensation: 10m @@ -927,63 +931,63 @@ sensor: i2c_id: i2c_bus - platform: sgp30 eco2: - name: "Workshop eCO2" + name: Workshop eCO2 accuracy_decimals: 1 tvoc: - name: "Workshop TVOC" + name: Workshop TVOC accuracy_decimals: 1 address: 0x58 update_interval: 5s i2c_id: i2c_bus - platform: sps30 pm_1_0: - name: "Workshop PM <1µm Weight concentration" - id: "workshop_PM_1_0" + name: Workshop PM <1µm Weight concentration + id: workshop_PM_1_0 pm_2_5: - name: "Workshop PM <2.5µm Weight concentration" - id: "workshop_PM_2_5" + name: Workshop PM <2.5µm Weight concentration + id: workshop_PM_2_5 pm_4_0: - name: "Workshop PM <4µm Weight concentration" - id: "workshop_PM_4_0" + name: Workshop PM <4µm Weight concentration + id: workshop_PM_4_0 pm_10_0: - name: "Workshop PM <10µm Weight concentration" - id: "workshop_PM_10_0" + name: Workshop PM <10µm Weight concentration + id: workshop_PM_10_0 pmc_0_5: - name: "Workshop PM <0.5µm Number concentration" - id: "workshop_PMC_0_5" + name: Workshop PM <0.5µm Number concentration + id: workshop_PMC_0_5 pmc_1_0: - name: "Workshop PM <1µm Number concentration" - id: "workshop_PMC_1_0" + name: Workshop PM <1µm Number concentration + id: workshop_PMC_1_0 pmc_2_5: - name: "Workshop PM <2.5µm Number concentration" - id: "workshop_PMC_2_5" + name: Workshop PM <2.5µm Number concentration + id: workshop_PMC_2_5 pmc_4_0: - name: "Workshop PM <4µm Number concentration" - id: "workshop_PMC_4_0" + name: Workshop PM <4µm Number concentration + id: workshop_PMC_4_0 pmc_10_0: - name: "Workshop PM <10µm Number concentration" - id: "workshop_PMC_10_0" + name: Workshop PM <10µm Number concentration + id: workshop_PMC_10_0 address: 0x69 update_interval: 10s i2c_id: i2c_bus - platform: sht4x temperature: - name: "SHT4X Temperature" + name: SHT4X Temperature humidity: - name: "SHT4X Humidity" + name: SHT4X Humidity address: 0x44 update_interval: 15s i2c_id: i2c_bus - platform: shtcx temperature: - name: "Living Room Temperature 10" + name: Living Room Temperature 10 humidity: - name: "Living Room Humidity 10" + name: Living Room Humidity 10 address: 0x70 update_interval: 15s i2c_id: i2c_bus - platform: template - name: "Template Sensor" + name: Template Sensor state_class: measurement id: template_sensor lambda: |- @@ -1001,7 +1005,7 @@ sensor: id: template_sensor state: !lambda "return NAN;" - platform: tsl2561 - name: "TSL2561 Ambient Light" + name: TSL2561 Ambient Light address: 0x39 update_interval: 15s is_cs_package: true @@ -1015,17 +1019,17 @@ sensor: integration_time: 600ms gain: high visible: - name: "tsl2591 visible" + name: tsl2591 visible id: tsl2591_vis - unit_of_measurement: "pH" + unit_of_measurement: pH infrared: - name: "tsl2591 infrared" + name: tsl2591 infrared id: tsl2591_ir full_spectrum: - name: "tsl2591 full_spectrum" + name: tsl2591 full_spectrum id: tsl2591_fs calculated_lux: - name: "tsl2591 calculated_lux" + name: tsl2591 calculated_lux id: tsl2591_cl i2c_id: i2c_bus - platform: ultrasonic @@ -1033,17 +1037,17 @@ sensor: echo_pin: number: GPIO23 inverted: true - name: "Ultrasonic Sensor" + name: Ultrasonic Sensor timeout: 5.5m id: ultrasonic_sensor1 - platform: uptime name: Uptime Sensor - platform: wifi_signal - name: "WiFi Signal Sensor" + name: WiFi Signal Sensor update_interval: 15s - platform: mqtt_subscribe - name: "MQTT Subscribe Sensor 1" - topic: "mqtt/topic" + name: MQTT Subscribe Sensor 1 + topic: mqtt/topic id: the_sensor qos: 2 on_value: @@ -1055,9 +1059,9 @@ sensor: - platform: sds011 uart_id: uart0 pm_2_5: - name: "SDS011 PM2.5" + name: SDS011 PM2.5 pm_10_0: - name: "SDS011 PM10.0" + name: SDS011 PM10.0 update_interval: 5min rx_only: false - platform: ccs811 @@ -1070,9 +1074,9 @@ sensor: i2c_id: i2c_bus - platform: tx20 wind_speed: - name: "Windspeed" + name: Windspeed wind_direction_degrees: - name: "Winddirection Degrees" + name: Winddirection Degrees pin: number: GPIO04 mode: INPUT @@ -1080,48 +1084,48 @@ sensor: clock_pin: GPIO5 data_pin: GPIO4 co2: - name: "ZyAura CO2" + name: ZyAura CO2 temperature: - name: "ZyAura Temperature" + name: ZyAura Temperature humidity: - name: "ZyAura Humidity" + name: ZyAura Humidity - platform: as3935 lightning_energy: - name: "Lightning Energy" + name: Lightning Energy distance: - name: "Distance Storm" + name: Distance Storm - platform: tmp117 - name: "TMP117 Temperature" + name: TMP117 Temperature update_interval: 5s i2c_id: i2c_bus - platform: hm3301 pm_1_0: - name: "PM1.0" + name: PM1.0 pm_2_5: - name: "PM2.5" + name: PM2.5 pm_10_0: - name: "PM10.0" + name: PM10.0 aqi: - name: "AQI" - calculation_type: "CAQI" + name: AQI + calculation_type: CAQI i2c_id: i2c_bus - platform: teleinfo - tag_name: "HCHC" - name: "hchc" - unit_of_measurement: "Wh" + tag_name: HCHC + name: hchc + unit_of_measurement: Wh icon: mdi:flash teleinfo_id: myteleinfo - platform: mcp9808 - name: "MCP9808 Temperature" + name: MCP9808 Temperature update_interval: 15s i2c_id: i2c_bus - platform: ezo id: ph_ezo address: 99 - unit_of_measurement: "pH" + unit_of_measurement: pH i2c_id: i2c_bus - platform: sdp3x - name: "HVAC Filter Pressure drop" + name: HVAC Filter Pressure drop id: filter_pressure update_interval: 5s accuracy_decimals: 3 @@ -1129,11 +1133,11 @@ sensor: - platform: cs5460a id: cs5460a1 current: - name: "Socket current" + name: Socket current voltage: - name: "Mains voltage" + name: Mains voltage power: - name: "Socket power" + name: Socket power on_value: then: cs5460a.restart: cs5460a1 @@ -1141,8 +1145,8 @@ sensor: pga_gain: 10X current_gain: 0.01 voltage_gain: 0.000573 - current_hpf: on - voltage_hpf: on + current_hpf: true + voltage_hpf: true phase_offset: 20 pulse_energy: 0.01 kWh cs_pin: @@ -1151,7 +1155,7 @@ sensor: - platform: max9611 i2c_id: i2c_bus shunt_resistance: 0.2 ohm - gain: "1X" + gain: 1X voltage: name: Max9611 Voltage current: @@ -1163,7 +1167,7 @@ sensor: update_interval: 1s esp32_touch: - setup_mode: False + setup_mode: false iir_filter: 10ms sleep_duration: 27ms measurement_duration: 8ms @@ -1180,7 +1184,7 @@ binary_sensor: number: 1 # One of INPUT or INPUT_PULLUP mode: INPUT_PULLUP - inverted: False + inverted: false - platform: gpio name: "MCP23S17 Pin #1" pin: @@ -1189,7 +1193,7 @@ binary_sensor: number: 1 # One of INPUT or INPUT_PULLUP mode: INPUT_PULLUP - inverted: False + inverted: false - platform: gpio name: "MCP23S17 Pin #1 with interrupt" pin: @@ -1198,11 +1202,11 @@ binary_sensor: number: 1 # One of INPUT or INPUT_PULLUP mode: INPUT_PULLUP - inverted: False + inverted: false interrupt: FALLING - platform: gpio pin: GPIO9 - name: "Living Room Window" + name: Living Room Window device_class: window filters: - invert: @@ -1242,7 +1246,7 @@ binary_sensor: - OFF for at least 0.2s then: - logger.log: - format: "Multi Clicked TWO" + format: Multi Clicked TWO level: warn - timing: - OFF for 1s to 2s @@ -1250,30 +1254,30 @@ binary_sensor: - OFF for at least 0.5s then: - logger.log: - format: "Multi Clicked LONG SINGLE" + format: Multi Clicked LONG SINGLE level: warn - timing: - ON for at most 1s - OFF for at least 0.5s then: - logger.log: - format: "Multi Clicked SINGLE" + format: Multi Clicked SINGLE level: warn id: binary_sensor1 - platform: gpio pin: number: GPIO9 mode: INPUT_PULLUP - name: "Living Room Window 2" + name: Living Room Window 2 - platform: status - name: "Living Room Status" + name: Living Room Status - platform: esp32_touch - name: "ESP32 Touch Pad GPIO27" + name: ESP32 Touch Pad GPIO27 pin: GPIO27 threshold: 1000 id: btn_left - platform: template - name: "Garage Door Open" + name: Garage Door Open id: garage_door lambda: |- if (isnan(id(${sensorname}_sensor).state)) { @@ -1291,7 +1295,7 @@ binary_sensor: on_press: - binary_sensor.template.publish: id: garage_door - state: OFF + state: false - output.ledc.set_frequency: id: gpio_19 frequency: 500.0Hz @@ -1301,41 +1305,41 @@ binary_sensor: - platform: pn532 pn532_id: pn532_bs uid: 74-10-37-94 - name: "PN532 NFC Tag" + name: PN532 NFC Tag - platform: rdm6300 uid: 7616525 - name: "RDM6300 NFC Tag" + name: RDM6300 NFC Tag - platform: gpio - name: "PCF binary sensor" + name: PCF binary sensor pin: pcf8574: pcf8574_hub number: 1 mode: INPUT - inverted: True + inverted: true - platform: gpio - name: "MCP21 binary sensor" + name: MCP21 binary sensor pin: mcp23xxx: mcp23017_hub number: 1 mode: INPUT - inverted: True + inverted: true - platform: gpio - name: "MCP22 binary sensor" + name: MCP22 binary sensor pin: mcp23xxx: mcp23008_hub number: 7 mode: INPUT_PULLUP - inverted: False + inverted: false - platform: gpio - name: "MCP23 binary sensor" + name: MCP23 binary sensor pin: mcp23016: mcp23016_hub number: 7 mode: INPUT - inverted: False + inverted: false - platform: remote_receiver - name: "Raw Remote Receiver Test" + name: Raw Remote Receiver Test raw: code: [ @@ -1376,7 +1380,7 @@ binary_sensor: 1709, ] - platform: as3935 - name: "Storm Alert" + name: Storm Alert - platform: analog_threshold name: Analog Trheshold 1 sensor_id: template_sensor @@ -1434,7 +1438,7 @@ output: pin: GPIO26 id: gpio_26 power_supply: atx_power_supply - inverted: False + inverted: false - platform: ledc pin: 19 id: gpio_19 @@ -1468,67 +1472,67 @@ output: - platform: tlc59208f id: tlc_0 channel: 0 - tlc59208f_id: "tlc59208f_1" + tlc59208f_id: tlc59208f_1 - platform: tlc59208f id: tlc_1 channel: 1 - tlc59208f_id: "tlc59208f_1" + tlc59208f_id: tlc59208f_1 - platform: tlc59208f id: tlc_2 channel: 2 - tlc59208f_id: "tlc59208f_1" + tlc59208f_id: tlc59208f_1 - platform: tlc59208f id: tlc_3 channel: 0 - tlc59208f_id: "tlc59208f_2" + tlc59208f_id: tlc59208f_2 - platform: tlc59208f id: tlc_4 channel: 1 - tlc59208f_id: "tlc59208f_2" + tlc59208f_id: tlc59208f_2 - platform: tlc59208f id: tlc_5 channel: 2 - tlc59208f_id: "tlc59208f_2" + tlc59208f_id: tlc59208f_2 - platform: tlc59208f id: tlc_6 channel: 0 - tlc59208f_id: "tlc59208f_3" + tlc59208f_id: tlc59208f_3 - platform: tlc59208f id: tlc_7 channel: 1 - tlc59208f_id: "tlc59208f_3" + tlc59208f_id: tlc59208f_3 - platform: tlc59208f id: tlc_8 channel: 2 - tlc59208f_id: "tlc59208f_3" + tlc59208f_id: tlc59208f_3 - platform: gpio id: id2 pin: pcf8574: pcf8574_hub number: 0 mode: OUTPUT - inverted: False + inverted: false - platform: gpio id: id22 pin: mcp23xxx: mcp23017_hub number: 0 mode: OUTPUT - inverted: False + inverted: false - platform: gpio id: id23 pin: mcp23xxx: mcp23008_hub number: 0 mode: OUTPUT - inverted: False + inverted: false - platform: gpio id: id25 pin: mcp23016: mcp23016_hub number: 0 mode: OUTPUT - inverted: False + inverted: false - platform: my9231 id: my_0 channel: 0 @@ -1588,27 +1592,27 @@ e131: light: - platform: binary - name: "Desk Lamp" + name: Desk Lamp output: gpio_26 effects: - strobe: - strobe: - name: "My Strobe" + name: My Strobe colors: - - state: True + - state: true duration: 250ms - - state: False + - state: false duration: 250ms on_turn_on: - switch.template.publish: id: livingroom_lights - state: yes + state: true on_turn_off: - switch.template.publish: id: livingroom_lights - state: yes + state: true - platform: monochromatic - name: "Kitchen Lights" + name: Kitchen Lights id: kitchen output: gpio_19 gamma_correct: 2.8 @@ -1617,7 +1621,7 @@ light: - strobe: - flicker: - flicker: - name: "My Flicker" + name: My Flicker alpha: 98% intensity: 1.5% - lambda: @@ -1629,20 +1633,20 @@ light: if (state == 4) state = 0; - platform: rgb - name: "Living Room Lights" + name: Living Room Lights id: ${roomname}_lights red: pca_0 green: pca_1 blue: pca_2 - platform: rgbw - name: "Living Room Lights 2" + name: Living Room Lights 2 red: pca_3 green: pca_4 blue: pca_5 white: pca_6 color_interlock: true - platform: rgbww - name: "Living Room Lights 2" + name: Living Room Lights 2 red: pca_3 green: pca_4 blue: pca_5 @@ -1652,7 +1656,7 @@ light: warm_white_color_temperature: 500 mireds color_interlock: true - platform: rgbct - name: "Living Room Lights 2" + name: Living Room Lights 2 red: pca_3 green: pca_4 blue: pca_5 @@ -1662,14 +1666,14 @@ light: warm_white_color_temperature: 500 mireds color_interlock: true - platform: cwww - name: "Living Room Lights 2" + name: Living Room Lights 2 cold_white: pca_6 warm_white: pca_6 cold_white_color_temperature: 153 mireds warm_white_color_temperature: 500 mireds constant_brightness: true - platform: color_temperature - name: "Living Room Lights 2" + name: Living Room Lights 2 color_temperature: pca_6 brightness: pca_6 cold_white_color_temperature: 153 mireds @@ -1683,7 +1687,7 @@ light: max_refresh_rate: 20ms power_supply: atx_power_supply color_correct: [75%, 100%, 50%] - name: "FastLED WS2811 Light" + name: FastLED WS2811 Light effects: - addressable_color_wipe: - addressable_color_wipe: @@ -1698,7 +1702,7 @@ light: blue: 0% num_leds: 1 add_led_interval: 100ms - reverse: False + reverse: false - addressable_scan: - addressable_scan: name: Scan Effect With Custom Values @@ -1726,7 +1730,7 @@ light: update_interval: 16ms intensity: 5% - addressable_lambda: - name: "Test For Custom Lambda Effect" + name: Test For Custom Lambda Effect lambda: |- if (initial_run) { it[0] = current_color; @@ -1762,10 +1766,10 @@ light: data_rate: 2MHz num_leds: 60 rgb_order: BRG - name: "FastLED SPI Light" + name: FastLED SPI Light - platform: neopixelbus id: addr3 - name: "Neopixelbus Light" + name: Neopixelbus Light gamma_correct: 2.8 color_correct: [0.0, 0.0, 0.0, 0.0] default_transition_length: 10s @@ -1781,7 +1785,7 @@ light: num_leds: 60 pin: GPIO23 - platform: partition - name: "Partition Light" + name: Partition Light segments: - id: addr1 from: 0 @@ -1795,13 +1799,13 @@ light: - single_light_id: ${roomname}_lights - platform: shelly_dimmer - name: "Shelly Dimmer Light" + name: Shelly Dimmer Light power: - name: "Shelly Dimmer Power" + name: Shelly Dimmer Power voltage: - name: "Shelly Dimmer Voltage" + name: Shelly Dimmer Voltage current: - name: "Shelly Dimmer Current" + name: Shelly Dimmer Current max_brightness: 500 firmware: "51.6" uart_id: uart0 @@ -1813,8 +1817,8 @@ remote_transmitter: climate: - platform: tcl112 name: TCL112 Climate With Sensor - supports_heat: True - supports_cool: True + supports_heat: true + supports_cool: true sensor: ${sensorname}_sensor - platform: tcl112 name: TCL112 Climate @@ -1836,8 +1840,8 @@ climate: target_temperature_state_topic: target/temperature/state/topic - platform: coolix name: Coolix Climate With Sensor - supports_heat: True - supports_cool: True + supports_heat: true + supports_cool: true sensor: ${sensorname}_sensor - platform: coolix name: Coolix Climate @@ -1871,7 +1875,7 @@ climate: use_fahrenheit: true - platform: midea on_state: - logger.log: "State changed!" + logger.log: State changed! id: midea_unit uart_id: uart0 name: Midea Climate @@ -1905,11 +1909,11 @@ climate: - HORIZONTAL - BOTH outdoor_temperature: - name: "Temp" + name: Temp power_usage: - name: "Power" + name: Power humidity_setpoint: - name: "Humidity" + name: Humidity - platform: anova name: Anova cooker ble_client_id: ble_blah @@ -1955,7 +1959,7 @@ switch: # Use pin number 0 number: 0 mode: OUTPUT - inverted: False + inverted: false - platform: gpio name: "MCP23S17 Pin #0" pin: @@ -1963,12 +1967,12 @@ switch: # Use pin number 0 number: 1 mode: OUTPUT - inverted: False + inverted: false - platform: gpio pin: GPIO25 - name: "Living Room Dehumidifier" + name: Living Room Dehumidifier icon: "mdi:restart" - inverted: True + inverted: true command_topic: custom_command_topic command_retain: true restore_mode: ALWAYS_OFF @@ -2045,20 +2049,20 @@ switch: remote_transmitter.transmit_rc_switch_type_a: group: "11001" device: "01000" - state: True + state: true protocol: pulse_length: 175 sync: [1, 31] zero: [1, 3] one: [3, 1] - inverted: False + inverted: false - platform: template name: RC Switch Type B turn_on_action: remote_transmitter.transmit_rc_switch_type_b: address: 4 channel: 2 - state: True + state: true - platform: template name: RC Switch Type C turn_on_action: @@ -2066,14 +2070,14 @@ switch: family: "a" group: 1 device: 2 - state: True + state: true - platform: template name: RC Switch Type D turn_on_action: remote_transmitter.transmit_rc_switch_type_d: group: "a" device: 2 - state: True + state: true - platform: template name: RC5 turn_on_action: @@ -2091,12 +2095,49 @@ switch: turn_on_action: remote_transmitter.transmit_aeha: address: 0x8008 - data: [0x00, 0x02, 0xFD, 0xFF, 0x00, 0x33, 0xCC, 0x49, 0xB6, 0xC8, 0x37, 0x16, 0xE9, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xCA, 0x35, 0x8F, 0x70, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF] + data: + [ + 0x00, + 0x02, + 0xFD, + 0xFF, + 0x00, + 0x33, + 0xCC, + 0x49, + 0xB6, + 0xC8, + 0x37, + 0x16, + 0xE9, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0xCA, + 0x35, + 0x8F, + 0x70, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0x00, + 0xFF, + 0x00, + 0xFF, + ] - platform: template name: Living Room Lights id: livingroom_lights - optimistic: True - assumed_state: yes + optimistic: true + assumed_state: true turn_on_action: - switch.turn_on: living_room_lights_on - output.set_level: @@ -2119,22 +2160,22 @@ switch: level: !lambda "return 0.5;" turn_off_action: - switch.turn_on: living_room_lights_off - restore_state: False + restore_state: false on_turn_on: - switch.template.publish: id: livingroom_lights - state: yes + state: true - platform: restart - name: "Living Room Restart" + name: Living Room Restart - platform: safe_mode - name: "Living Room Restart (Safe Mode)" + name: Living Room Restart (Safe Mode) - platform: shutdown - name: "Living Room Shutdown" + name: Living Room Shutdown - platform: output - name: "Generic Output" + name: Generic Output output: pca_6 - platform: template - name: "Template Switch" + name: Template Switch id: my_switch lambda: |- if (id(binary_sensor1).state) { @@ -2152,27 +2193,27 @@ switch: // Switch is OFF, do something else here } optimistic: true - assumed_state: no - restore_state: True + assumed_state: false + restore_state: true on_turn_off: - switch.template.publish: id: my_switch state: !lambda "return false;" - platform: uart uart_id: uart0 - name: "UART String Output" - data: "DataToSend" + name: UART String Output + data: DataToSend - platform: uart uart_id: uart0 - name: "UART Bytes Output" + name: UART Bytes Output data: [0xDE, 0xAD, 0xBE, 0xEF] - platform: uart uart_id: uart0 - name: "UART Recurring Output" + name: UART Recurring Output data: [0xDE, 0xAD, 0xBE, 0xEF] send_every: 1s - platform: template - assumed_state: yes + assumed_state: true name: Stepper Switch turn_on_action: - stepper.set_target: @@ -2194,7 +2235,7 @@ switch: sn74hc595: sn74hc595_hub # Use pin number 0 number: 0 - inverted: False + inverted: false - platform: template id: ble1_status optimistic: true @@ -2206,7 +2247,7 @@ switch: fan: - platform: binary output: gpio_26 - name: "Living Room Fan 1" + name: Living Room Fan 1 oscillation_output: gpio_19 direction_output: gpio_26 - platform: speed @@ -2214,7 +2255,7 @@ fan: icon: mdi:weather-windy output: pca_6 speed_count: 10 - name: "Living Room Fan 2" + name: Living Room Fan 2 oscillation_output: gpio_19 direction_output: gpio_26 oscillation_state_topic: oscillation/state/topic @@ -2225,13 +2266,13 @@ fan: speed_command_topic: speed/command/topic on_speed_set: then: - - logger.log: "Fan speed was changed!" + - logger.log: Fan speed was changed! - platform: bedjet name: My Bedjet fan bedjet_id: my_bedjet_client - platform: copy source_id: fan_speed - name: "Fan Speed Copy" + name: Fan Speed Copy interval: - interval: 10s @@ -2242,6 +2283,7 @@ interval: - display.page.show_previous: display1 - interval: 2s then: + # yamllint disable rule:line-length - lambda: |- static uint16_t btn_left_state = id(btn_left)->get_value(); @@ -2250,13 +2292,14 @@ interval: btn_left_state = ((uint32_t) id(btn_left)->get_value() + 63 * (uint32_t)btn_left_state) >> 6; id(btn_left)->set_threshold(btn_left_state * 0.9); + # yamllint enable rule:line-length - if: condition: display.is_displaying_page: id: display1 page_id: page1 then: - - logger.log: "Seeing page 1" + - logger.log: Seeing page 1 color: - id: kbx_red @@ -2328,7 +2371,7 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: ssd1306_i2c - model: "SSD1306_128X64" + model: SSD1306_128X64 reset_pin: GPIO23 address: 0x3C id: display1 @@ -2349,28 +2392,28 @@ display: ESP_LOGD("display", "1 -> 2"); i2c_id: i2c_bus - platform: ssd1306_spi - model: "SSD1306 128x64" + model: SSD1306 128x64 cs_pin: GPIO23 dc_pin: GPIO23 reset_pin: GPIO23 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: ssd1322_spi - model: "SSD1322 256x64" + model: SSD1322 256x64 cs_pin: GPIO23 dc_pin: GPIO23 reset_pin: GPIO23 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: ssd1325_spi - model: "SSD1325 128x64" + model: SSD1325 128x64 cs_pin: GPIO23 dc_pin: GPIO23 reset_pin: GPIO23 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: ssd1327_i2c - model: "SSD1327 128X128" + model: SSD1327 128X128 reset_pin: GPIO23 address: 0x3D id: display1327 @@ -2384,7 +2427,7 @@ display: // Nothing i2c_id: i2c_bus - platform: ssd1327_spi - model: "SSD1327 128x128" + model: SSD1327 128x128 cs_pin: GPIO23 dc_pin: GPIO23 reset_pin: GPIO23 @@ -2397,7 +2440,7 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: ssd1351_spi - model: "SSD1351 128x128" + model: SSD1351 128x128 cs_pin: GPIO23 dc_pin: GPIO23 reset_pin: GPIO23 @@ -2420,7 +2463,7 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: st7735 - model: "INITR_BLACKTAB" + model: INITR_BLACKTAB cs_pin: GPIO5 dc_pin: GPIO16 reset_pin: GPIO23 @@ -2432,7 +2475,7 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: ili9341 - model: "TFT 2.4" + model: TFT 2.4 cs_pin: GPIO5 dc_pin: GPIO4 reset_pin: GPIO22 @@ -2442,7 +2485,7 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: ili9341 - model: "TFT 2.4" + model: TFT 2.4 cs_pin: GPIO5 dc_pin: GPIO4 reset_pin: GPIO22 @@ -2529,7 +2572,7 @@ rc522_i2c: mcp4728: - id: mcp4728_dac - store_in_eeprom: False + store_in_eeprom: false address: 0x60 i2c_id: i2c_bus @@ -2562,7 +2605,7 @@ time: cover: - platform: template - name: "Template Cover" + name: Template Cover id: template_cover lambda: |- if (id(binary_sensor1).state) { @@ -2575,8 +2618,8 @@ cover: - cover.template.publish: id: template_cover state: CLOSED - assumed_state: no - has_position: yes + assumed_state: false + has_position: true position_state_topic: position/state/topic position_command_topic: position/command/topic tilt_lambda: !lambda "return 0.5;" @@ -2589,12 +2632,12 @@ cover: then: - lambda: 'ESP_LOGD("cover", "closed");' - platform: am43 - name: "Test AM43" + name: Test AM43 id: am43_test ble_client_id: ble_foo icon: mdi:blinds - platform: feedback - name: "Feedback Cover" + name: Feedback Cover id: gate device_class: gate @@ -2638,24 +2681,24 @@ tca9548a: i2c_id: multiplex0_chan0 pcf8574: - - id: "pcf8574_hub" + - id: pcf8574_hub address: 0x21 - pcf8575: False + pcf8575: false i2c_id: i2c_bus mcp23017: - - id: "mcp23017_hub" - open_drain_interrupt: "true" + - id: mcp23017_hub + open_drain_interrupt: true i2c_id: i2c_bus mcp23008: - - id: "mcp23008_hub" + - id: mcp23008_hub address: 0x22 - open_drain_interrupt: "true" + open_drain_interrupt: true i2c_id: i2c_bus mcp23016: - - id: "mcp23016_hub" + - id: mcp23016_hub address: 0x23 i2c_id: i2c_bus @@ -2672,29 +2715,29 @@ stepper: globals: - id: glob_int type: int - restore_value: yes + restore_value: true initial_value: "0" - id: glob_float type: float - restore_value: yes + restore_value: true initial_value: "0.0f" - id: glob_bool type: bool - restore_value: no + restore_value: false initial_value: "true" - id: glob_string type: std::string - restore_value: no + restore_value: false # initial_value: "" - id: glob_bool_processed type: bool - restore_value: no + restore_value: false initial_value: "false" text_sensor: - platform: ble_client ble_client_id: ble_foo - name: "Sensor Location" + name: Sensor Location service_uuid: "180d" characteristic_uuid: "2a38" descriptor_uuid: "2902" @@ -2705,7 +2748,7 @@ text_sensor: - lambda: |- ESP_LOGD("green_btn", "Location changed: %s", x.c_str()); - platform: mqtt_subscribe - name: "MQTT Subscribe Text" + name: MQTT Subscribe Text topic: "the/topic" qos: 2 on_value: @@ -2742,25 +2785,25 @@ text_sensor: id: ${textname}_text - platform: wifi_info scan_results: - name: "Scan Results" + name: Scan Results ip_address: - name: "IP Address" + name: IP Address ssid: - name: "SSID" + name: SSID bssid: - name: "BSSID" + name: BSSID mac_address: - name: "Mac Address" + name: Mac Address - platform: version - name: "ESPHome Version No Timestamp" - hide_timestamp: True + name: ESPHome Version No Timestamp + hide_timestamp: true - platform: teleinfo - tag_name: "OPTARIF" - name: "optarif" + tag_name: OPTARIF + name: optarif teleinfo_id: myteleinfo sn74hc595: - - id: "sn74hc595_hub" + - id: sn74hc595_hub data_pin: GPIO21 clock_pin: GPIO23 latch_pin: GPIO22 @@ -2883,7 +2926,7 @@ qr_code: lock: - platform: template id: test_lock1 - name: "Template Switch" + name: Template Switch lambda: |- if (id(binary_sensor1).state) { return LOCK_STATE_LOCKED; @@ -2891,7 +2934,7 @@ lock: return LOCK_STATE_UNLOCKED; } optimistic: true - assumed_state: no + assumed_state: false on_unlock: - lock.template.publish: id: test_lock1 @@ -2901,7 +2944,7 @@ lock: id: test_lock1 state: !lambda "return LOCK_STATE_LOCKED;" - platform: output - name: "Generic Output Lock" + name: Generic Output Lock id: test_lock2 output: pca_6 - platform: copy @@ -2910,7 +2953,7 @@ lock: button: - platform: template - name: "Start calibration" + name: Start calibration on_press: - scd4x.perform_forced_calibration: value: 419 diff --git a/tests/test2.yaml b/tests/test2.yaml index 3dd8f824dd..d01a0a742a 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -1,3 +1,4 @@ +--- esphome: name: $devicename platform: ESP32 @@ -28,7 +29,7 @@ api: i2c: sda: 21 scl: 22 - scan: False + scan: false spi: clk_pin: GPIO21 @@ -47,7 +48,7 @@ uart: - lambda: UARTDebug::log_hex(direction, bytes, ':'); ota: - safe_mode: True + safe_mode: true port: 3286 num_attempts: 15 @@ -67,7 +68,7 @@ as3935_i2c: irq_pin: GPIO12 mcp3008: - - id: 'mcp3008_hub' + - id: mcp3008_hub cs_pin: GPIO12 output: @@ -86,35 +87,35 @@ sensor: id: ha_hello_world_temperature - platform: ble_rssi mac_address: AC:37:43:77:5F:4C - name: 'BLE Google Home Mini RSSI value' + name: BLE Google Home Mini RSSI value - platform: ble_rssi - service_uuid: '11aa' - name: 'BLE Test Service 16' + service_uuid: 11aa + name: BLE Test Service 16 - platform: ble_rssi - service_uuid: '11223344' - name: 'BLE Test Service 32' + service_uuid: "11223344" + name: BLE Test Service 32 - platform: ble_rssi - service_uuid: '11223344-5566-7788-99aa-bbccddeeff00' - name: 'BLE Test Service 128' + service_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + name: BLE Test Service 128 - platform: ble_rssi - service_uuid: '11223344-5566-7788-99aa-bbccddeeff00' - name: 'BLE Test iBeacon UUID' + service_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + name: BLE Test iBeacon UUID - platform: b_parasite mac_address: F0:CA:F0:CA:01:01 humidity: - name: 'b-parasite Air Humidity' + name: b-parasite Air Humidity temperature: - name: 'b-parasite Air Temperature' + name: b-parasite Air Temperature moisture: - name: 'b-parasite Soil Moisture' + name: b-parasite Soil Moisture battery_voltage: - name: 'b-parasite Battery Voltage' + name: b-parasite Battery Voltage illuminance: - name: 'b-parasite Illuminance' + name: b-parasite Illuminance - platform: senseair id: senseair0 co2: - name: 'SenseAir CO2 Value' + name: SenseAir CO2 Value on_value: then: - senseair.background_calibration: senseair0 @@ -126,167 +127,167 @@ sensor: - platform: ruuvitag mac_address: FF:56:D3:2F:7D:E8 humidity: - name: 'RuuviTag Humidity' + name: RuuviTag Humidity temperature: - name: 'RuuviTag Temperature' + name: RuuviTag Temperature pressure: - name: 'RuuviTag Pressure' + name: RuuviTag Pressure acceleration_x: - name: 'RuuviTag Acceleration X' + name: RuuviTag Acceleration X acceleration_y: - name: 'RuuviTag Acceleration Y' + name: RuuviTag Acceleration Y acceleration_z: - name: 'RuuviTag Acceleration Z' + name: RuuviTag Acceleration Z battery_voltage: - name: 'RuuviTag Battery Voltage' + name: RuuviTag Battery Voltage tx_power: - name: 'RuuviTag TX Power' + name: RuuviTag TX Power movement_counter: - name: 'RuuviTag Movement Counter' + name: RuuviTag Movement Counter measurement_sequence_number: - name: 'RuuviTag Measurement Sequence Number' + name: RuuviTag Measurement Sequence Number - platform: as3935 lightning_energy: - name: 'Lightning Energy' + name: Lightning Energy distance: - name: 'Distance Storm' + name: Distance Storm - platform: xiaomi_hhccjcy01 mac_address: 94:2B:FF:5C:91:61 temperature: - name: 'Xiaomi HHCCJCY01 Temperature' + name: Xiaomi HHCCJCY01 Temperature moisture: - name: 'Xiaomi HHCCJCY01 Moisture' + name: Xiaomi HHCCJCY01 Moisture illuminance: - name: 'Xiaomi HHCCJCY01 Illuminance' + name: Xiaomi HHCCJCY01 Illuminance conductivity: - name: 'Xiaomi HHCCJCY01 Soil Conductivity' + name: Xiaomi HHCCJCY01 Soil Conductivity battery_level: - name: 'Xiaomi HHCCJCY01 Battery Level' + name: Xiaomi HHCCJCY01 Battery Level - platform: xiaomi_lywsdcgq mac_address: 7A:80:8E:19:36:BA temperature: - name: 'Xiaomi LYWSDCGQ Temperature' + name: Xiaomi LYWSDCGQ Temperature humidity: - name: 'Xiaomi LYWSDCGQ Humidity' + name: Xiaomi LYWSDCGQ Humidity battery_level: - name: 'Xiaomi LYWSDCGQ Battery Level' + name: Xiaomi LYWSDCGQ Battery Level - platform: xiaomi_lywsd02 mac_address: 3F:5B:7D:82:58:4E temperature: - name: 'Xiaomi LYWSD02 Temperature' + name: Xiaomi LYWSD02 Temperature humidity: - name: 'Xiaomi LYWSD02 Humidity' + name: Xiaomi LYWSD02 Humidity battery_level: - name: 'Xiaomi LYWSD02 Battery Level' + name: Xiaomi LYWSD02 Battery Level - platform: xiaomi_cgg1 mac_address: 7A:80:8E:19:36:BA temperature: - name: 'Xiaomi CGG1 Temperature' + name: Xiaomi CGG1 Temperature humidity: - name: 'Xiaomi CGG1 Humidity' + name: Xiaomi CGG1 Humidity battery_level: - name: 'Xiaomi CGG1 Battery Level' + name: Xiaomi CGG1 Battery Level - platform: xiaomi_gcls002 - mac_address: '94:2B:FF:5C:91:61' + mac_address: 94:2B:FF:5C:91:61 temperature: - name: 'GCLS02 Temperature' + name: GCLS02 Temperature moisture: - name: 'GCLS02 Moisture' + name: GCLS02 Moisture conductivity: - name: 'GCLS02 Soil Conductivity' + name: GCLS02 Soil Conductivity illuminance: - name: 'GCLS02 Illuminance' + name: GCLS02 Illuminance - platform: xiaomi_hhccpot002 - mac_address: '94:2B:FF:5C:91:61' + mac_address: 94:2B:FF:5C:91:61 moisture: - name: 'HHCCPOT002 Moisture' + name: HHCCPOT002 Moisture conductivity: - name: 'HHCCPOT002 Soil Conductivity' + name: HHCCPOT002 Soil Conductivity - platform: xiaomi_lywsd03mmc - mac_address: 'A4:C1:38:4E:16:78' - bindkey: 'e9efaa6873f9f9c87a5e75a5f814801c' + mac_address: A4:C1:38:4E:16:78 + bindkey: e9efaa6873f9f9c87a5e75a5f814801c temperature: - name: 'Xiaomi LYWSD03MMC Temperature' + name: Xiaomi LYWSD03MMC Temperature humidity: - name: 'Xiaomi LYWSD03MMC Humidity' + name: Xiaomi LYWSD03MMC Humidity battery_level: - name: 'Xiaomi LYWSD03MMC Battery Level' + name: Xiaomi LYWSD03MMC Battery Level - platform: xiaomi_cgd1 - mac_address: 'A4:C1:38:D1:61:7D' - bindkey: 'c99d2313182473b38001086febf781bd' + mac_address: A4:C1:38:D1:61:7D + bindkey: c99d2313182473b38001086febf781bd temperature: - name: 'Xiaomi CGD1 Temperature' + name: Xiaomi CGD1 Temperature humidity: - name: 'Xiaomi CGD1 Humidity' + name: Xiaomi CGD1 Humidity battery_level: - name: 'Xiaomi CGD1 Battery Level' + name: Xiaomi CGD1 Battery Level - platform: xiaomi_jqjcy01ym - mac_address: '7A:80:8E:19:36:BA' + mac_address: 7A:80:8E:19:36:BA temperature: - name: 'JQJCY01YM Temperature' + name: JQJCY01YM Temperature humidity: - name: 'JQJCY01YM Humidity' + name: JQJCY01YM Humidity formaldehyde: - name: 'JQJCY01YM Formaldehyde' + name: JQJCY01YM Formaldehyde battery_level: - name: 'JQJCY01YM Battery Level' + name: JQJCY01YM Battery Level - platform: xiaomi_mhoc303 - mac_address: 'E7:50:59:32:A0:1C' + mac_address: E7:50:59:32:A0:1C temperature: - name: 'MHO-C303 Temperature' + name: MHO-C303 Temperature humidity: - name: 'MHO-C303 Humidity' + name: MHO-C303 Humidity battery_level: - name: 'MHO-C303 Battery Level' + name: MHO-C303 Battery Level - platform: atc_mithermometer - mac_address: 'A4:C1:38:4E:16:78' + mac_address: A4:C1:38:4E:16:78 temperature: - name: 'ATC Temperature' + name: ATC Temperature humidity: - name: 'ATC Humidity' + name: ATC Humidity battery_level: - name: 'ATC Battery-Level' + name: ATC Battery-Level battery_voltage: - name: 'ATC Battery-Voltage' + name: ATC Battery-Voltage - platform: pvvx_mithermometer - mac_address: 'A4:C1:38:4E:16:78' + mac_address: A4:C1:38:4E:16:78 temperature: - name: 'PVVX Temperature' + name: PVVX Temperature humidity: - name: 'PVVX Humidity' + name: PVVX Humidity battery_level: - name: 'PVVX Battery-Level' + name: PVVX Battery-Level battery_voltage: - name: 'PVVX Battery-Voltage' + name: PVVX Battery-Voltage - platform: inkbird_ibsth1_mini mac_address: 38:81:D7:0A:9C:11 temperature: - name: 'Inkbird IBS-TH1 Temperature' + name: Inkbird IBS-TH1 Temperature humidity: - name: 'Inkbird IBS-TH1 Humidity' + name: Inkbird IBS-TH1 Humidity battery_level: - name: 'Inkbird IBS-TH1 Battery Level' + name: Inkbird IBS-TH1 Battery Level - platform: xiaomi_rtcgq02lm id: motion_rtcgq02lm battery_level: - name: 'Mi Motion Sensor 2 Battery level' + name: Mi Motion Sensor 2 Battery level - platform: ltr390 uv: - name: "LTR390 UV" + name: LTR390 UV uv_index: - name: "LTR390 UVI" + name: LTR390 UVI light: - name: "LTR390 Light" + name: LTR390 Light ambient_light: - name: "LTR390 ALS" - gain: "X3" + name: LTR390 ALS + gain: X3 resolution: 18 window_correction_factor: 1.0 address: 0x53 update_interval: 60s - platform: sgp4x voc: - name: "VOC Index" + name: VOC Index id: sgp40_voc_index algorithm_tuning: index_offset: 100 @@ -296,7 +297,7 @@ sensor: std_initial: 50 gain_factor: 230 nox: - name: "NOx" + name: NOx algorithm_tuning: index_offset: 100 learning_time_offset_hours: 12 @@ -307,7 +308,7 @@ sensor: update_interval: 5s - platform: mcp3008 update_interval: 5s - mcp3008_id: 'mcp3008_hub' + mcp3008_id: mcp3008_hub id: freezer_temp_source reference_voltage: 3.19 number: 0 @@ -315,59 +316,59 @@ sensor: ble_client_id: airthings01 update_interval: 5min temperature: - name: "Wave Plus Temperature" + name: Wave Plus Temperature radon: - name: "Wave Plus Radon" + name: Wave Plus Radon radon_long_term: - name: "Wave Plus Radon Long Term" + name: Wave Plus Radon Long Term pressure: - name: "Wave Plus Pressure" + name: Wave Plus Pressure humidity: - name: "Wave Plus Humidity" + name: Wave Plus Humidity co2: - name: "Wave Plus CO2" + name: Wave Plus CO2 tvoc: - name: "Wave Plus VOC" + name: Wave Plus VOC - platform: airthings_wave_mini ble_client_id: airthingsmini01 update_interval: 5min temperature: - name: "Wave Mini Temperature" + name: Wave Mini Temperature humidity: - name: "Wave Mini Humidity" + name: Wave Mini Humidity pressure: - name: "Wave Mini Pressure" + name: Wave Mini Pressure tvoc: - name: "Wave Mini VOC" + name: Wave Mini VOC - platform: ina260 address: 0x40 current: - name: "INA260 Current" + name: INA260 Current power: - name: "INA260 Power" + name: INA260 Power bus_voltage: - name: "INA260 Voltage" + name: INA260 Voltage update_interval: 60s - platform: radon_eye_rd200 ble_client_id: radon_eye_ble_id update_interval: 10min radon: - name: "RD200 Radon" + name: RD200 Radon radon_long_term: - name: "RD200 Radon Long Term" + name: RD200 Radon Long Term - platform: mopeka_pro_check mac_address: D3:75:F2:DC:16:91 tank_type: CUSTOM custom_distance_full: 40cm custom_distance_empty: 10mm temperature: - name: "Propane test temp" + name: Propane test temp level: - name: "Propane test level" + name: Propane test level distance: - name: "Propane test distance" + name: Propane test distance battery_level: - name: "Propane test battery level" + name: Propane test battery level time: - platform: homeassistant @@ -377,7 +378,7 @@ time: - logger.log: It's 16:00 esp32_touch: - setup_mode: True + setup_mode: true binary_sensor: - platform: homeassistant @@ -389,76 +390,80 @@ binary_sensor: id: ha_hello_world_binary_attribute - platform: ble_presence mac_address: AC:37:43:77:5F:4C - name: 'ESP32 BLE Tracker Google Home Mini' + name: ESP32 BLE Tracker Google Home Mini - platform: ble_presence - service_uuid: '11aa' - name: 'BLE Test Service 16 Presence' + service_uuid: 11aa + name: BLE Test Service 16 Presence - platform: ble_presence - service_uuid: '11223344' - name: 'BLE Test Service 32 Presence' + service_uuid: "11223344" + name: BLE Test Service 32 Presence - platform: ble_presence - service_uuid: '11223344-5566-7788-99aa-bbccddeeff00' - name: 'BLE Test Service 128 Presence' + service_uuid: 11223344-5566-7788-99aa-bbccddeeff00 + name: BLE Test Service 128 Presence - platform: ble_presence - ibeacon_uuid: '11223344-5566-7788-99aa-bbccddeeff00' + ibeacon_uuid: 11223344-5566-7788-99aa-bbccddeeff00 ibeacon_major: 100 ibeacon_minor: 1 - name: 'BLE Test iBeacon Presence' + name: BLE Test iBeacon Presence - platform: esp32_touch - name: 'ESP32 Touch Pad GPIO27' + name: ESP32 Touch Pad GPIO27 pin: GPIO27 threshold: 1000 - platform: as3935 - name: 'Storm Alert' + name: Storm Alert - platform: xiaomi_mue4094rt - name: 'MUE4094RT Motion' - mac_address: '7A:80:8E:19:36:BA' - timeout: '5s' + name: MUE4094RT Motion + mac_address: 7A:80:8E:19:36:BA + timeout: 5s - platform: xiaomi_mjyd02yla - name: 'MJYD02YL-A Motion' - mac_address: '50:EC:50:CD:32:02' - bindkey: '48403ebe2d385db8d0c187f81e62cb64' + name: MJYD02YL-A Motion + mac_address: 50:EC:50:CD:32:02 + bindkey: 48403ebe2d385db8d0c187f81e62cb64 idle_time: - name: 'MJYD02YL-A Idle Time' + name: MJYD02YL-A Idle Time light: - name: 'MJYD02YL-A Light Status' + name: MJYD02YL-A Light Status battery_level: - name: 'MJYD02YL-A Battery Level' + name: MJYD02YL-A Battery Level - platform: xiaomi_wx08zm - name: 'WX08ZM Activation State' - mac_address: '74:a3:4a:b5:07:34' + name: WX08ZM Activation State + mac_address: 74:a3:4a:b5:07:34 tablet: - name: 'WX08ZM Tablet Resource' + name: WX08ZM Tablet Resource battery_level: - name: 'WX08ZM Battery Level' + name: WX08ZM Battery Level - platform: xiaomi_cgpr1 - name: 'CGPR1 Motion' - mac_address: '12:34:56:12:34:56' - bindkey: '48403ebe2d385db8d0c187f81e62cb64' + name: CGPR1 Motion + mac_address: "12:34:56:12:34:56" + bindkey: 48403ebe2d385db8d0c187f81e62cb64 battery_level: - name: 'CGPR1 battery Level' + name: CGPR1 battery Level idle_time: - name: 'CGPR1 Idle Time' + name: CGPR1 Idle Time illuminance: - name: 'CGPR1 Illuminance' + name: CGPR1 Illuminance - platform: xiaomi_rtcgq02lm id: motion_rtcgq02lm motion: - name: 'Mi Motion Sensor 2' + name: Mi Motion Sensor 2 light: - name: 'Mi Motion Sensor 2 Light' + name: Mi Motion Sensor 2 Light button: - name: 'Mi Motion Sensor 2 Button' + name: Mi Motion Sensor 2 Button esp32_ble_tracker: on_ble_advertise: - mac_address: AC:37:43:77:5F:4C then: + # yamllint disable rule:line-length - lambda: !lambda |- ESP_LOGD("main", "The device address is %s", x.address_str().c_str()); + # yamllint enable rule:line-length - then: + # yamllint disable rule:line-length - lambda: !lambda |- ESP_LOGD("main", "The device address is %s", x.address_str().c_str()); + # yamllint enable rule:line-length on_ble_service_data_advertise: - service_uuid: ABCD then: @@ -494,16 +499,12 @@ xiaomi_rtcgq02lm: mac_address: 01:02:03:04:05:06 bindkey: '48403ebe2d385db8d0c187f81e62cb64' -#esp32_ble_beacon: -# type: iBeacon -# uuid: 'c29ce823-e67a-4e71-bff2-abaa32e77a98' - status_led: pin: GPIO2 text_sensor: - platform: version - name: 'ESPHome Version' + name: ESPHome Version icon: mdi:icon id: version_sensor on_value: @@ -511,8 +512,10 @@ text_sensor: condition: - api.connected: then: + # yamllint disable rule:line-length - lambda: !lambda |- ESP_LOGD("main", "The state is %s=%s", x.c_str(), id(version_sensor).state.c_str()); + # yamllint enable rule:line-length - script.execute: my_script - homeassistant.service: service: notify.html5 @@ -537,19 +540,19 @@ text_sensor: - deep_sleep.enter: sleep_duration: !lambda "return 30 * 60 * 1000;" - platform: template - name: 'Template Text Sensor' + name: Template Text Sensor lambda: |- return {"Hello World"}; filters: - to_upper: - to_lower: - - append: "xyz" - - prepend: "abcd" + - append: xyz + - prepend: abcd - substitute: - Hello -> Goodbye - map: - red -> green - - lambda: return {"1234"}; + - lambda: 'return {"1234"};' - platform: homeassistant entity_id: sensor.hello_world2 id: ha_hello_world2 @@ -587,7 +590,7 @@ stepper: pin_b: GPIO27 pin_c: GPIO25 pin_d: GPIO26 - sleep_when_done: no + sleep_when_done: false step_mode: HALF_STEP max_speed: 250 steps/s @@ -598,7 +601,7 @@ stepper: interval: interval: 5s then: - - logger.log: 'Interval Run' + - logger.log: Interval Run display: @@ -611,7 +614,7 @@ cap1188: switch: - platform: template - name: "Test BLE Write Action" + name: Test BLE Write Action turn_on_action: - ble_client.ble_write: id: airthings01 diff --git a/tests/test3.yaml b/tests/test3.yaml index 4b2224dc3c..de0fc0dfdb 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -1,3 +1,4 @@ +--- esphome: name: $device_name comment: $device_comment @@ -9,38 +10,38 @@ esphome: - wifi.connected - time.has_time then: - - logger.log: "Have time" + - logger.log: Have time includes: - custom.h esp8266: board: d1_mini - early_pin_init: True + early_pin_init: true substitutions: device_name: test3 device_comment: test3 device - min_sub: '0.03' - max_sub: '12.0%' + min_sub: "0.03" + max_sub: "12.0%" api: port: 8000 - password: 'pwd' + password: pwd reboot_timeout: 0min encryption: - key: 'bOFFzzvfpg5DB94DuBGLXD/hMnhpDKgP9UQyBulwWVU=' + key: bOFFzzvfpg5DB94DuBGLXD/hMnhpDKgP9UQyBulwWVU= services: - service: hello_world variables: name: string then: - logger.log: - format: 'Hello World %s!' + format: Hello World %s! args: - name.c_str() - service: empty_service then: - - logger.log: 'Service Called' + - logger.log: Service Called - service: all_types variables: bool_: bool @@ -48,7 +49,7 @@ api: float_: float string_: string then: - - logger.log: 'Something happened' + - logger.log: Something happened - stepper.set_target: id: my_stepper2 target: !lambda 'return int_;' @@ -60,7 +61,9 @@ api: string_arr: string[] then: - logger.log: + # yamllint disable rule:line-length format: 'Bool: %s (%u), Int: %d (%u), Float: %f (%u), String: %s (%u)' + # yamllint enable rule:line-length args: - YESNO(bool_arr[0]) - bool_arr.size() @@ -104,7 +107,7 @@ api: then: - dfplayer.play_folder: folder: !lambda 'return folder;' - loop: True + loop: true - service: dfplayer_set_device variables: @@ -122,7 +125,9 @@ api: variables: preset: int then: + # yamllint disable rule:line-length - dfplayer.set_eq: !lambda 'return static_cast(preset);' + # yamllint enable rule:line-length - service: dfplayer_sleep then: @@ -220,7 +225,7 @@ wifi: i2c: sda: 4 scl: 5 - scan: False + scan: false spi: clk_pin: GPIO12 @@ -231,7 +236,7 @@ uart: - id: uart1 tx_pin: number: GPIO1 - inverted: yes + inverted: true rx_pin: GPIO3 baud_rate: 115200 - id: uart2 @@ -279,7 +284,7 @@ modbus: uart_id: uart1 ota: - safe_mode: True + safe_mode: true port: 3286 reboot_timeout: 15min @@ -302,40 +307,40 @@ adalight: sensor: - platform: daly_bms voltage: - name: "Battery Voltage" + name: Battery Voltage current: - name: "Battery Current" + name: Battery Current battery_level: - name: "Battery Level" + name: Battery Level max_cell_voltage: - name: "Max Cell Voltage" + name: Max Cell Voltage max_cell_voltage_number: - name: "Max Cell Voltage Number" + name: Max Cell Voltage Number min_cell_voltage: - name: "Min Cell Voltage" + name: Min Cell Voltage min_cell_voltage_number: - name: "Min Cell Voltage Number" + name: Min Cell Voltage Number max_temperature: - name: "Max Temperature" + name: Max Temperature max_temperature_probe_number: - name: "Max Temperature Probe Number" + name: Max Temperature Probe Number min_temperature: - name: "Min Temperature" + name: Min Temperature min_temperature_probe_number: - name: "Min Temperature Probe Number" + name: Min Temperature Probe Number remaining_capacity: - name: "Remaining Capacity" + name: Remaining Capacity cells_number: - name: "Cells Number" + name: Cells Number temperature_1: - name: "Temperature 1" + name: Temperature 1 temperature_2: - name: "Temperature 2" + name: Temperature 2 - platform: apds9960 type: proximity name: APDS9960 Proximity - platform: vl53l0x - name: 'VL53L0x Distance' + name: VL53L0x Distance address: 0x29 update_interval: 60s enable_pin: GPIO13 @@ -357,32 +362,32 @@ sensor: id: ha_hello_world - platform: aht10 temperature: - name: 'Temperature' + name: Temperature humidity: - name: 'Humidity' + name: Humidity - platform: am2320 temperature: - name: 'Temperature' + name: Temperature humidity: - name: 'Humidity' + name: Humidity - platform: hydreon_rgxx - model: "RG 9" + model: RG 9 uart_id: uart6 - id: "hydreon_rg9" + id: hydreon_rg9 moisture: - name: "hydreon_rain" + name: hydreon_rain id: hydreon_rain - platform: hydreon_rgxx - model: "RG_15" + model: RG_15 uart_id: uart6 acc: - name: "hydreon_acc" + name: hydreon_acc event_acc: - name: "hydreon_event_acc" + name: hydreon_event_acc total_acc: - name: "hydreon_total_acc" + name: hydreon_total_acc r_int: - name: "hydreon_r_int" + name: hydreon_r_int - platform: adc pin: VCC id: my_sensor @@ -496,281 +501,281 @@ sensor: - platform: bl0939 uart_id: uart8 voltage: - name: 'BL0939 Voltage' + name: BL0939 Voltage current_1: - name: 'BL0939 Current 1' + name: BL0939 Current 1 current_2: - name: 'BL0939 Current 2' + name: BL0939 Current 2 active_power_1: - name: 'BL0939 Active Power 1' + name: BL0939 Active Power 1 active_power_2: - name: 'BL0939 Active Power 2' + name: BL0939 Active Power 2 energy_1: - name: 'BL0939 Energy 1' + name: BL0939 Energy 1 energy_2: - name: 'BL0939 Energy 2' + name: BL0939 Energy 2 energy_total: - name: 'BL0939 Total energy' + name: BL0939 Total energy - platform: bl0940 uart_id: uart3 voltage: - name: 'BL0940 Voltage' + name: BL0940 Voltage current: - name: 'BL0940 Current' + name: BL0940 Current power: - name: 'BL0940 Power' + name: BL0940 Power energy: - name: 'BL0940 Energy' + name: BL0940 Energy internal_temperature: - name: 'BL0940 Internal temperature' + name: BL0940 Internal temperature external_temperature: - name: 'BL0940 External temperature' + name: BL0940 External temperature - platform: pzem004t uart_id: uart3 voltage: - name: 'PZEM004T Voltage' + name: PZEM004T Voltage current: - name: 'PZEM004T Current' + name: PZEM004T Current power: - name: 'PZEM004T Power' + name: PZEM004T Power - platform: pzemac id: pzemac1 voltage: - name: 'PZEMAC Voltage' + name: PZEMAC Voltage current: - name: 'PZEMAC Current' + name: PZEMAC Current power: - name: 'PZEMAC Power' + name: PZEMAC Power energy: - name: 'PZEMAC Energy' + name: PZEMAC Energy frequency: - name: 'PZEMAC Frequency' + name: PZEMAC Frequency power_factor: - name: 'PZEMAC Power Factor' + name: PZEMAC Power Factor - platform: pzemdc voltage: - name: 'PZEMDC Voltage' + name: PZEMDC Voltage current: - name: 'PZEMDC Current' + name: PZEMDC Current power: - name: 'PZEMDC Power' + name: PZEMDC Power - platform: tmp102 - name: 'TMP102 Temperature' + name: TMP102 Temperature - platform: hm3301 pm_1_0: - name: 'PM1.0' + name: PM1.0 pm_2_5: - name: 'PM2.5' + name: PM2.5 pm_10_0: - name: 'PM10.0' + name: PM10.0 aqi: - name: 'AQI' - calculation_type: 'AQI' + name: AQI + calculation_type: AQI - platform: pmsx003 uart_id: uart9 type: PMSX003 pm_1_0: - name: 'PM 1.0 Concentration' + name: PM 1.0 Concentration pm_2_5: - name: 'PM 2.5 Concentration' + name: PM 2.5 Concentration pm_10_0: - name: 'PM 10.0 Concentration' + name: PM 10.0 Concentration pm_1_0_std: - name: 'PM 1.0 Standard Atmospher Concentration' + name: PM 1.0 Standard Atmospher Concentration pm_2_5_std: - name: 'PM 2.5 Standard Atmospher Concentration' + name: PM 2.5 Standard Atmospher Concentration pm_10_0_std: - name: 'PM 10.0 Standard Atmospher Concentration' + name: PM 10.0 Standard Atmospher Concentration pm_0_3um: - name: 'Particulate Count >0.3um' + name: Particulate Count >0.3um pm_0_5um: - name: 'Particulate Count >0.5um' + name: Particulate Count >0.5um pm_1_0um: - name: 'Particulate Count >1.0um' + name: Particulate Count >1.0um pm_2_5um: - name: 'Particulate Count >2.5um' + name: Particulate Count >2.5um pm_5_0um: - name: 'Particulate Count >5.0um' + name: Particulate Count >5.0um pm_10_0um: - name: 'Particulate Count >10.0um' + name: Particulate Count >10.0um update_interval: 30s - platform: pmsx003 uart_id: uart5 type: PMS5003T pm_2_5: - name: 'PM 2.5 Concentration' + name: PM 2.5 Concentration temperature: - name: 'PMS Temperature' + name: PMS Temperature humidity: - name: 'PMS Humidity' + name: PMS Humidity - platform: pmsx003 uart_id: uart6 type: PMS5003ST pm_1_0: - name: 'PM 1.0 Concentration' + name: PM 1.0 Concentration pm_2_5: - name: 'PM 2.5 Concentration' + name: PM 2.5 Concentration pm_10_0: - name: 'PM 10.0 Concentration' + name: PM 10.0 Concentration pm_1_0_std: - name: 'PM 1.0 Standard Atmospher Concentration' + name: PM 1.0 Standard Atmospher Concentration pm_2_5_std: - name: 'PM 2.5 Standard Atmospher Concentration' + name: PM 2.5 Standard Atmospher Concentration pm_10_0_std: - name: 'PM 10.0 Standard Atmospher Concentration' + name: PM 10.0 Standard Atmospher Concentration pm_0_3um: - name: 'Particulate Count >0.3um' + name: Particulate Count >0.3um pm_0_5um: - name: 'Particulate Count >0.5um' + name: Particulate Count >0.5um pm_1_0um: - name: 'Particulate Count >1.0um' + name: Particulate Count >1.0um pm_2_5um: - name: 'Particulate Count >2.5um' + name: Particulate Count >2.5um pm_5_0um: - name: 'Particulate Count >5.0um' + name: Particulate Count >5.0um pm_10_0um: - name: 'Particulate Count >10.0um' + name: Particulate Count >10.0um temperature: - name: 'PMS Temperature' + name: PMS Temperature humidity: - name: 'PMS Humidity' + name: PMS Humidity formaldehyde: - name: 'PMS Formaldehyde Concentration' + name: PMS Formaldehyde Concentration - platform: cse7761 uart_id: uart7 voltage: - name: 'CSE7761 Voltage' + name: CSE7761 Voltage current_1: - name: 'CSE7761 Current 1' + name: CSE7761 Current 1 current_2: - name: 'CSE7761 Current 2' + name: CSE7761 Current 2 active_power_1: - name: 'CSE7761 Active Power 1' + name: CSE7761 Active Power 1 active_power_2: - name: 'CSE7761 Active Power 2' + name: CSE7761 Active Power 2 - platform: cse7766 uart_id: uart3 voltage: - name: 'CSE7766 Voltage' + name: CSE7766 Voltage current: - name: 'CSE7766 Current' + name: CSE7766 Current power: - name: 'CSE776 Power' + name: CSE776 Power - platform: ezo id: ph_ezo address: 99 - unit_of_measurement: 'pH' + unit_of_measurement: pH - platform: tof10120 - name: "Distance sensor" + name: Distance sensor update_interval: 5s - platform: fingerprint_grow fingerprint_count: - name: "Fingerprint Count" + name: Fingerprint Count status: - name: "Fingerprint Status" + name: Fingerprint Status capacity: - name: "Fingerprint Capacity" + name: Fingerprint Capacity security_level: - name: "Fingerprint Security Level" + name: Fingerprint Security Level last_finger_id: - name: "Fingerprint Last Finger ID" + name: Fingerprint Last Finger ID last_confidence: - name: "Fingerprint Last Confidence" + name: Fingerprint Last Confidence - platform: sdm_meter phase_a: current: - name: 'Phase A Current' + name: Phase A Current voltage: - name: 'Phase A Voltage' + name: Phase A Voltage active_power: - name: 'Phase A Power' + name: Phase A Power power_factor: - name: 'Phase A Power Factor' + name: Phase A Power Factor apparent_power: - name: 'Phase A Apparent Power' + name: Phase A Apparent Power reactive_power: - name: 'Phase A Reactive Power' + name: Phase A Reactive Power phase_angle: - name: 'Phase A Phase Angle' + name: Phase A Phase Angle phase_b: current: - name: 'Phase B Current' + name: Phase B Current voltage: - name: 'Phase B Voltage' + name: Phase B Voltage active_power: - name: 'Phase B Power' + name: Phase B Power power_factor: - name: 'Phase B Power Factor' + name: Phase B Power Factor apparent_power: - name: 'Phase B Apparent Power' + name: Phase B Apparent Power reactive_power: - name: 'Phase B Reactive Power' + name: Phase B Reactive Power phase_angle: - name: 'Phase B Phase Angle' + name: Phase B Phase Angle phase_c: current: - name: 'Phase C Current' + name: Phase C Current voltage: - name: 'Phase C Voltage' + name: Phase C Voltage active_power: - name: 'Phase C Power' + name: Phase C Power power_factor: - name: 'Phase C Power Factor' + name: Phase C Power Factor apparent_power: - name: 'Phase C Apparent Power' + name: Phase C Apparent Power reactive_power: - name: 'Phase C Reactive Power' + name: Phase C Reactive Power phase_angle: - name: 'Phase C Phase Angle' + name: Phase C Phase Angle frequency: - name: 'Frequency' + name: Frequency import_active_energy: - name: 'Import Active Energy' + name: Import Active Energy export_active_energy: - name: 'Export Active Energy' + name: Export Active Energy import_reactive_energy: - name: 'Import Reactive Energy' + name: Import Reactive Energy export_reactive_energy: - name: 'Export Reactive Energy' + name: Export Reactive Energy - platform: dsmr energy_delivered_tariff1: name: dsmr_energy_delivered_tariff1 - platform: nextion id: testnumber - name: 'testnumber' + name: testnumber variable_name: testnumber - platform: nextion id: testwave - name: 'testwave' + name: testwave component_id: 2 wave_channel_id: 1 - platform: mlx90393 oversampling: 1 filter: 0 - gain: "3X" + gain: 3X x_axis: - name: "mlxxaxis" + name: mlxxaxis y_axis: - name: "mlxyaxis" + name: mlxyaxis z_axis: - name: "mlxzaxis" + name: mlxzaxis resolution: 17BIT temperature: - name: "mlxtemp" + name: mlxtemp oversampling: 2 - platform: smt100 uart_id: uart10 counts: - name: "Counts" + name: Counts dielectric_constant: - name: "Dielectric Constant" + name: Dielectric Constant temperature: - name: "Temperature" + name: Temperature moisture: - name: "Moisture" + name: Moisture voltage: - name: "Voltage" + name: Voltage update_interval: 60s time: - platform: homeassistant @@ -786,9 +791,9 @@ mpr121: binary_sensor: - platform: daly_bms charging_mos_enabled: - name: "Charging MOS" + name: Charging MOS discharging_mos_enabled: - name: "Discharging MOS" + name: Discharging MOS - platform: apds9960 direction: up name: APDS9960 Up @@ -816,18 +821,18 @@ binary_sensor: - platform: mpr121 id: touchkey0 channel: 0 - name: 'touchkey0' + name: touchkey0 - platform: mpr121 channel: 1 - name: 'touchkey1' + name: touchkey1 id: bin1 - platform: mpr121 channel: 2 - name: 'touchkey2' + name: touchkey2 id: bin2 - platform: mpr121 channel: 3 - name: 'touchkey3' + name: touchkey3 id: bin3 on_press: then: @@ -839,7 +844,7 @@ binary_sensor: channel: 1 name: TTP229 BSF Test - platform: fingerprint_grow - name: "Fingerprint Enrolling" + name: Fingerprint Enrolling - platform: custom lambda: |- auto s = new CustomBinarySensor(); @@ -851,27 +856,27 @@ binary_sensor: - platform: nextion page_id: 0 component_id: 2 - name: 'Nextion Component 2 Touch' + name: Nextion Component 2 Touch - platform: nextion id: r0_sensor - name: 'R0 Sensor' + name: R0 Sensor component_name: page0.r0 - platform: template - id: 'cover_toggle' + id: cover_toggle on_press: then: - cover.toggle: time_based_cover - cover.toggle: endstop_cover - platform: hydreon_rgxx - hydreon_rgxx_id: "hydreon_rg9" + hydreon_rgxx_id: hydreon_rg9 too_cold: - name: "rg9_toocold" + name: rg9_toocold em_sat: - name: "rg9_emsat" + name: rg9_emsat lens_bad: - name: "rg9_lens_bad" + name: rg9_lens_bad - platform: template - id: 'pzemac_reset_energy' + id: pzemac_reset_energy on_press: then: - pzemac.reset_energy: pzemac1 @@ -891,14 +896,16 @@ status_led: text_sensor: - platform: daly_bms status: - name: "BMS Status" + name: BMS Status - platform: version - name: 'ESPHome Version' + name: ESPHome Version icon: mdi:icon id: version_sensor on_value: + # yamllint disable rule:line-length - lambda: !lambda |- ESP_LOGD("main", "The state is %s=%s", x.c_str(), id(version_sensor).state.c_str()); + # yamllint enable rule:line-length - script.execute: my_script - script.wait: my_script - script.stop: my_script @@ -912,7 +919,7 @@ text_sensor: my_variable: |- return id(version_sensor).state; - platform: template - name: 'Template Text Sensor' + name: Template Text Sensor lambda: |- return {"Hello World"}; - platform: homeassistant @@ -933,9 +940,9 @@ text_sensor: component_name: text0 - platform: dsmr identification: - name: "dsmr_identification" + name: dsmr_identification p1_version: - name: "dsmr_p1_version" + name: dsmr_p1_version script: - id: my_script @@ -948,9 +955,9 @@ sm2135: switch: - platform: template - name: 'mpr121_toggle' + name: mpr121_toggle id: mpr121_toggle - optimistic: True + optimistic: true - platform: gpio id: gpio_switch1 pin: @@ -978,7 +985,7 @@ switch: name: Custom Switch - platform: nextion id: r0 - name: 'R0 Switch' + name: R0 Switch component_name: page0.r0 custom_component: @@ -994,7 +1001,7 @@ stepper: pin_b: GPIO13 pin_c: GPIO14 pin_d: GPIO15 - sleep_when_done: no + sleep_when_done: false step_mode: HALF_STEP max_speed: 250 steps/s acceleration: inf @@ -1010,7 +1017,7 @@ stepper: interval: interval: 5s then: - - logger.log: 'Interval Run' + - logger.log: Interval Run - stepper.set_target: id: my_stepper2 target: 500 @@ -1123,7 +1130,7 @@ climate: default_target_temperature_high: 20°C - platform: pid id: pid_climate - name: 'PID Climate Controller' + name: PID Climate Controller sensor: ha_hello_world default_target_temperature: 21°C heat_output: my_slow_pwm @@ -1134,42 +1141,42 @@ climate: sprinkler: - id: yard_sprinkler_ctrlr - main_switch: "Yard Sprinklers" - auto_advance_switch: "Yard Sprinklers Auto Advance" - reverse_switch: "Yard Sprinklers Reverse" + main_switch: Yard Sprinklers + auto_advance_switch: Yard Sprinklers Auto Advance + reverse_switch: Yard Sprinklers Reverse pump_start_pump_delay: 2s pump_stop_valve_delay: 4s pump_switch_off_during_valve_open_delay: true valve_open_delay: 5s valves: - - valve_switch: "Yard Valve 0" - enable_switch: "Enable Yard Valve 0" + - valve_switch: Yard Valve 0 + enable_switch: Enable Yard Valve 0 pump_switch_id: gpio_switch1 run_duration: 10s valve_switch_id: gpio_switch2 - - valve_switch: "Yard Valve 1" - enable_switch: "Enable Yard Valve 1" + - valve_switch: Yard Valve 1 + enable_switch: Enable Yard Valve 1 pump_switch_id: gpio_switch1 run_duration: 10s valve_switch_id: gpio_switch2 - - valve_switch: "Yard Valve 2" - enable_switch: "Enable Yard Valve 2" + - valve_switch: Yard Valve 2 + enable_switch: Enable Yard Valve 2 pump_switch_id: gpio_switch1 run_duration: 10s valve_switch_id: gpio_switch2 - id: garden_sprinkler_ctrlr - main_switch: "Garden Sprinklers" - auto_advance_switch: "Garden Sprinklers Auto Advance" - reverse_switch: "Garden Sprinklers Reverse" + main_switch: Garden Sprinklers + auto_advance_switch: Garden Sprinklers Auto Advance + reverse_switch: Garden Sprinklers Reverse valve_overlap: 5s valves: - - valve_switch: "Garden Valve 0" - enable_switch: "Enable Garden Valve 0" + - valve_switch: Garden Valve 0 + enable_switch: Enable Garden Valve 0 pump_switch_id: gpio_switch1 run_duration: 10s valve_switch_id: gpio_switch2 - - valve_switch: "Garden Valve 1" - enable_switch: "Enable Garden Valve 1" + - valve_switch: Garden Valve 1 + enable_switch: Enable Garden Valve 1 pump_switch_id: gpio_switch1 run_duration: 10s valve_switch_id: gpio_switch2 @@ -1219,7 +1226,7 @@ cover: - switch.turn_on: gpio_switch2 close_duration: 4.5min - platform: current_based - name: "Current Based Cover" + name: Current Based Cover open_sensor: ade7953_current_a open_moving_current_threshold: 0.5 open_obstacle_current_threshold: 0.8 @@ -1240,7 +1247,7 @@ cover: malfunction_detection: true malfunction_action: then: - - logger.log: "Malfunction Detected" + - logger.log: Malfunction Detected - platform: template name: Template Cover with Tilt tilt_lambda: 'return 0.5;' @@ -1326,7 +1333,7 @@ light: pin_b: out2 - platform: sonoff_d1 uart_id: uart2 - use_rm433_remote: False + use_rm433_remote: false name: Sonoff D1 Dimmer id: d1_light restore_mode: RESTORE_DEFAULT_OFF @@ -1352,7 +1359,7 @@ sim800l: str = sender; str = message; - sim800l.send_sms: - message: 'hello you' + message: hello you recipient: '+1234' - sim800l.dial: recipient: '+1234' @@ -1365,7 +1372,7 @@ dfplayer: condition: not: dfplayer.is_playing then: - logger.log: 'Playback finished event' + logger.log: Playback finished event tm1651: id: tm1651_battery clk_pin: D6 @@ -1394,8 +1401,8 @@ rf_bridge: test = data.length; test = data.protocol; test_code = data.code; - - rf_bridge.start_advanced_sniffing - - rf_bridge.stop_advanced_sniffing + - rf_bridge.start_advanced_sniffing: + - rf_bridge.stop_advanced_sniffing: - rf_bridge.send_advanced_code: length: 0x04 protocol: 0x01 @@ -1413,13 +1420,13 @@ rf_bridge: json: key: !lambda |- return id(version_sensor).state; - greeting: 'Hello World' + greeting: Hello World - http_request.send: method: PUT url: https://esphome.io headers: Content-Type: application/json - body: 'Some data' + body: Some data verify_ssl: false display: @@ -1428,13 +1435,13 @@ display: num_chips: 4 rotate_chip: 0 intensity: 10 - scroll_mode: 'STOP' + scroll_mode: STOP id: my_matrix lambda: |- it.printdigit("hello"); - platform: nextion uart_id: uart1 - tft_url: 'http://esphome.io/default35.tft' + tft_url: http://esphome.io/default35.tft update_interval: 5s on_sleep: then: diff --git a/tests/test4.yaml b/tests/test4.yaml index c3079037c1..d6857de7cd 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -1,3 +1,4 @@ +--- esphome: name: $devicename platform: ESP32 @@ -25,7 +26,7 @@ api: i2c: sda: 21 scl: 22 - scan: False + scan: false spi: clk_pin: GPIO21 @@ -38,7 +39,7 @@ uart: baud_rate: 115200 ota: - safe_mode: True + safe_mode: true port: 3286 logger: @@ -71,7 +72,7 @@ select: 2: Both pipsolar: - id: inverter0 + id: inverter0 sx1509: - id: sx1509_hub @@ -81,9 +82,9 @@ mcp3204: cs_pin: GPIO23 dac7678: - address: 0x4A - id: dac7678_hub1 - internal_reference: true + address: 0x4A + id: dac7678_hub1 + internal_reference: true sensor: - platform: homeassistant @@ -226,22 +227,22 @@ sensor: pv_charging_power: id: inverter0_pv_charging_power name: inverter0_pv_charging_power - - platform: "hrxl_maxsonar_wr" - name: "Rainwater Tank Level" + - platform: hrxl_maxsonar_wr + name: Rainwater Tank Level filters: - sliding_window_moving_average: window_size: 12 send_every: 12 - or: - - throttle: "20min" - - delta: 0.02 + - throttle: 20min + - delta: 0.02 - platform: mcp3204 - name: "MCP3204 Pin 1" + name: MCP3204 Pin 1 number: 1 id: mcp_sensor - platform: copy source_id: mcp_sensor - name: "MCP binary sensor copy" + name: MCP binary sensor copy # # platform sensor.apds9960 requires component apds9960 @@ -321,15 +322,15 @@ binary_sensor: name: inverter0_backlight_on - platform: template id: ar1 - lambda: 'return {};' + lambda: "return {};" filters: - autorepeat: - - delay: 2s - time_off: 100ms - time_on: 900ms - - delay: 4s - time_off: 100ms - time_on: 400ms + - delay: 2s + time_off: 100ms + time_on: 900ms + - delay: 4s + time_off: 100ms + time_on: 400ms on_state: then: - lambda: 'ESP_LOGI("ar1:", "%d", x);' @@ -356,8 +357,7 @@ binary_sensor: y_min: 0 y_max: 100 on_press: - - logger.log: "Touched" - + - logger.log: Touched climate: - platform: tuya @@ -392,7 +392,7 @@ switch: light: - platform: fastled_clockless id: led_matrix_32x8 - name: "led_matrix_32x8" + name: led_matrix_32x8 chipset: WS2812B pin: GPIO15 num_leds: 256 @@ -417,7 +417,7 @@ cover: position_datapoint: 2 - platform: copy source_id: tuya_cover - name: "Tuya Cover copy" + name: Tuya Cover copy display: - platform: addressable_light @@ -516,7 +516,7 @@ text_sensor: name: inverter0_last_qflag - platform: copy source_id: inverter0_device_mode - name: "Inverter Text Sensor Copy" + name: Inverter Text Sensor Copy output: - platform: pipsolar @@ -524,37 +524,37 @@ output: battery_recharge_voltage: id: inverter0_battery_recharge_voltage_out - platform: dac7678 - dac7678_id: 'dac7678_hub1' + dac7678_id: dac7678_hub1 channel: 0 - id: 'dac7678_1_ch0' + id: dac7678_1_ch0 - platform: dac7678 - dac7678_id: 'dac7678_hub1' + dac7678_id: dac7678_hub1 channel: 1 - id: 'dac7678_1_ch1' + id: dac7678_1_ch1 - platform: dac7678 - dac7678_id: 'dac7678_hub1' + dac7678_id: dac7678_hub1 channel: 2 - id: 'dac7678_1_ch2' + id: dac7678_1_ch2 - platform: dac7678 - dac7678_id: 'dac7678_hub1' + dac7678_id: dac7678_hub1 channel: 3 - id: 'dac7678_1_ch3' + id: dac7678_1_ch3 - platform: dac7678 - dac7678_id: 'dac7678_hub1' + dac7678_id: dac7678_hub1 channel: 4 - id: 'dac7678_1_ch4' + id: dac7678_1_ch4 - platform: dac7678 - dac7678_id: 'dac7678_hub1' + dac7678_id: dac7678_hub1 channel: 5 - id: 'dac7678_1_ch5' + id: dac7678_1_ch5 - platform: dac7678 - dac7678_id: 'dac7678_hub1' + dac7678_id: dac7678_hub1 channel: 6 - id: 'dac7678_1_ch6' + id: dac7678_1_ch6 - platform: dac7678 - dac7678_id: 'dac7678_hub1' + dac7678_id: dac7678_hub1 channel: 7 - id: 'dac7678_1_ch7' + id: dac7678_1_ch7 esp32_camera: name: ESP-32 Camera data_pins: [GPIO17, GPIO35, GPIO34, GPIO5, GPIO39, GPIO18, GPIO36, GPIO19] @@ -581,9 +581,9 @@ esp32_camera_web_server: external_components: - source: github://esphome/esphome@dev refresh: 1d - components: ["bh1750"] + components: [bh1750] - source: ../esphome/components - components: ["sntp"] + components: [sntp] xpt2046: id: xpt_touchscreen cs_pin: 17 @@ -597,8 +597,9 @@ xpt2046: calibration_x_max: 280 calibration_y_min: 340 calibration_y_max: 3860 - swap_x_y: False + swap_x_y: false on_state: + # yamllint disable rule:line-length - lambda: |- ESP_LOGI("main", "args x=%d, y=%d, touched=%s", x, y, (touched ? "touch" : "release")); ESP_LOGI("main", "member x=%d, y=%d, touched=%d, x_raw=%d, y_raw=%d, z_raw=%d", @@ -609,6 +610,7 @@ xpt2046: id(xpt_touchscreen).y_raw, id(xpt_touchscreen).z_raw ); + # yamllint enable rule:line-length button: - platform: restart @@ -622,7 +624,6 @@ button: source_id: shutdown_btn name: Shutdown Button Copy - touchscreen: - platform: ektf2232 interrupt_pin: GPIO36 @@ -631,7 +632,7 @@ touchscreen: on_touch: - logger.log: format: Touch at (%d, %d) - args: ["touch.x", "touch.y"] + args: [touch.x, touch.y] - platform: lilygo_t5_47 id: lilygo_touchscreen @@ -640,7 +641,7 @@ touchscreen: on_touch: - logger.log: format: Touch at (%d, %d) - args: ["touch.x", "touch.y"] + args: [touch.x, touch.y] media_player: - platform: i2s_audio @@ -673,4 +674,4 @@ prometheus: relabel: ha_hello_world: id: hellow_world - name: "Hello World" + name: Hello World diff --git a/tests/test5.yaml b/tests/test5.yaml index c193009ead..3b6862a4d4 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -1,3 +1,4 @@ +--- esphome: name: test5 build_path: build/test5 @@ -60,8 +61,10 @@ mqtt: topic: testing/sensor/testing_sensor/state qos: 0 then: + # yamllint disable rule:line-length - lambda: |- - ESP_LOGD("Mqtt Test","testing/sensor/testing_sensor/state=[%s]",x.c_str()); + ESP_LOGD("Mqtt Test", "testing/sensor/testing_sensor/state=[%s]", x.c_str()); + # yamllint enable rule:line-length binary_sensor: - platform: gpio @@ -74,8 +77,8 @@ binary_sensor: id: modbus_binsensortest register_type: read address: 0x3200 - bitmask: 0x80 #(bit 8) - lambda: !lambda "{ return x ;}" + bitmask: 0x80 # (bit 8) + lambda: "return x;" tlc5947: data_pin: GPIO12 @@ -108,8 +111,8 @@ demo: esp32_ble: esp32_ble_server: - manufacturer: "ESPHome" - model: "Test5" + manufacturer: ESPHome + model: Test5 esp32_improv: authorizer: io0_button @@ -128,12 +131,12 @@ number: mode: slider on_value: - logger.log: - format: "Number changed to %f" - args: ["x"] + format: Number changed to %f + args: [x] set_action: - logger.log: - format: "Template Number set to %f" - args: ["x"] + format: Template Number set to %f + args: [x] - number.set: id: template_number_id value: 50 @@ -163,10 +166,10 @@ number: - id: modbus_numbertest platform: modbus_controller modbus_controller_id: modbus_controller_test - name: "ModbusNumber" + name: ModbusNumber address: 0x9002 value_type: U_WORD - lambda: "return x * 1.0; " + lambda: "return x * 1.0;" write_lambda: |- return x * 1.0 ; multiply: 1.0 @@ -180,11 +183,11 @@ select: restore_value: true on_value: - logger.log: - format: "Select changed to %s (index %d)" + format: Select changed to %s (index %d)" args: ["x.c_str()", "i"] set_action: - logger.log: - format: "Template Select set to %s" + format: Template Select set to %s args: ["x.c_str()"] - select.set: id: template_select_id @@ -216,7 +219,7 @@ select: - three - platform: modbus_controller - name: "Modbus Select Register 1000" + name: Modbus Select Register 1000 address: 1000 value_type: U_WORD optionsmap: @@ -228,41 +231,41 @@ select: sensor: - platform: selec_meter total_active_energy: - name: "SelecEM2M Total Active Energy" + name: SelecEM2M Total Active Energy import_active_energy: - name: "SelecEM2M Import Active Energy" + name: SelecEM2M Import Active Energy export_active_energy: - name: "SelecEM2M Export Active Energy" + name: SelecEM2M Export Active Energy total_reactive_energy: - name: "SelecEM2M Total Reactive Energy" + name: SelecEM2M Total Reactive Energy import_reactive_energy: - name: "SelecEM2M Import Reactive Energy" + name: SelecEM2M Import Reactive Energy export_reactive_energy: - name: "SelecEM2M Export Reactive Energy" + name: SelecEM2M Export Reactive Energy apparent_energy: - name: "SelecEM2M Apparent Energy" + name: SelecEM2M Apparent Energy active_power: - name: "SelecEM2M Active Power" + name: SelecEM2M Active Power reactive_power: - name: "SelecEM2M Reactive Power" + name: SelecEM2M Reactive Power apparent_power: - name: "SelecEM2M Apparent Power" + name: SelecEM2M Apparent Power voltage: - name: "SelecEM2M Voltage" + name: SelecEM2M Voltage current: - name: "SelecEM2M Current" + name: SelecEM2M Current power_factor: - name: "SelecEM2M Power Factor" + name: SelecEM2M Power Factor frequency: - name: "SelecEM2M Frequency" + name: SelecEM2M Frequency maximum_demand_active_power: - name: "SelecEM2M Maximum Demand Active Power" + name: SelecEM2M Maximum Demand Active Power disabled_by_default: true maximum_demand_reactive_power: - name: "SelecEM2M Maximum Demand Reactive Power" + name: SelecEM2M Maximum Demand Reactive Power disabled_by_default: true maximum_demand_apparent_power: - name: "SelecEM2M Maximum Demand Apparent Power" + name: SelecEM2M Maximum Demand Apparent Power disabled_by_default: true - id: modbus_sensortest @@ -279,41 +282,41 @@ sensor: - platform: bmp3xx temperature: - name: "BMP Temperature" + name: BMP Temperature oversampling: 16x pressure: - name: "BMP Pressure" + name: BMP Pressure address: 0x77 iir_filter: 2X - platform: sen5x id: sen54 temperature: - name: "Temperature" + name: Temperature accuracy_decimals: 1 humidity: - name: "Humidity" + name: Humidity accuracy_decimals: 0 pm_1_0: - name: " PM <1µm Weight concentration" + name: PM <1µm Weight concentration id: pm_1_0 accuracy_decimals: 1 pm_2_5: - name: " PM <2.5µm Weight concentration" + name: PM <2.5µm Weight concentration id: pm_2_5 accuracy_decimals: 1 pm_4_0: - name: " PM <4µm Weight concentration" + name: PM <4µm Weight concentration id: pm_4_0 accuracy_decimals: 1 pm_10_0: - name: " PM <10µm Weight concentration" + name: PM <10µm Weight concentration id: pm_10_0 accuracy_decimals: 1 nox: - name: "NOx" + name: NOx voc: - name: "VOC" + name: VOC algorithm_tuning: index_offset: 100 learning_time_offset_hours: 12 @@ -332,9 +335,9 @@ sensor: - platform: mcp9600 thermocouple_type: K hot_junction: - name: "Thermocouple Temperature" + name: Thermocouple Temperature cold_junction: - name: "Ambient Temperature" + name: Ambient Temperature script: - id: automation_test @@ -342,7 +345,7 @@ script: - repeat: count: 5 then: - - logger.log: "looping!" + - logger.log: looping! switch: - platform: modbus_controller diff --git a/tests/test_packages/test_packages_package1.yaml b/tests/test_packages/test_packages_package1.yaml index 0495984d42..312bbe574a 100644 --- a/tests/test_packages/test_packages_package1.yaml +++ b/tests/test_packages/test_packages_package1.yaml @@ -1,2 +1,3 @@ +--- sensor: - <<: !include ./test_uptime_sensor.yaml diff --git a/tests/test_packages/test_packages_package_wifi.yaml b/tests/test_packages/test_packages_package_wifi.yaml index 7d5d41ddab..a8c610edfd 100644 --- a/tests/test_packages/test_packages_package_wifi.yaml +++ b/tests/test_packages/test_packages_package_wifi.yaml @@ -1,4 +1,5 @@ +--- wifi: networks: - - ssid: 'WiFiFromPackage' - password: 'password1' + - ssid: "WiFiFromPackage" + password: "password1" diff --git a/tests/test_packages/test_uptime_sensor.yaml b/tests/test_packages/test_uptime_sensor.yaml index 1bf52a6d0b..f15d968fee 100644 --- a/tests/test_packages/test_uptime_sensor.yaml +++ b/tests/test_packages/test_uptime_sensor.yaml @@ -1,3 +1,4 @@ +--- # Uptime sensor. platform: uptime id: ${devicename}_uptime_pcg diff --git a/tests/unit_tests/fixtures/yaml_util/includes/scalar.yaml b/tests/unit_tests/fixtures/yaml_util/includes/scalar.yaml index ddd2156b5e..89879248aa 100644 --- a/tests/unit_tests/fixtures/yaml_util/includes/scalar.yaml +++ b/tests/unit_tests/fixtures/yaml_util/includes/scalar.yaml @@ -1 +1,2 @@ +--- ${var1} diff --git a/tests/unit_tests/fixtures/yaml_util/includetest.yaml b/tests/unit_tests/fixtures/yaml_util/includetest.yaml index 959283df60..af0a4e2030 100644 --- a/tests/unit_tests/fixtures/yaml_util/includetest.yaml +++ b/tests/unit_tests/fixtures/yaml_util/includetest.yaml @@ -8,10 +8,11 @@ wifi: !include name: my_custom_ssid esphome: - # should be substituted as 'original', not overwritten by vars in the !include above + # should be substituted as 'original', + # not overwritten by vars in the !include above name: ${name} name_add_mac_suffix: true platform: esp8266 - board: !include { file: includes/scalar.yaml, vars: { var1: nodemcu } } + board: !include {file: includes/scalar.yaml, vars: {var1: nodemcu}} - libraries: !include { file: includes/list.yaml, vars: { var1: Wire } } + libraries: !include {file: includes/list.yaml, vars: {var1: Wire}} From 635851807a660d401f60990520906d426e3bb6bc Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 6 Sep 2022 16:41:23 +1200 Subject: [PATCH 49/66] Fix HA addon auth using HA credentials (#3758) --- esphome/dashboard/dashboard.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index e7a17cba4c..889f5cc0bf 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -816,15 +816,16 @@ class LoginHandler(BaseHandler): import requests headers = { - "Authentication": f"Bearer {os.getenv('SUPERVISOR_TOKEN')}", + "X-Supervisor-Token": os.getenv("SUPERVISOR_TOKEN"), } + data = { "username": self.get_argument("username", ""), "password": self.get_argument("password", ""), } try: req = requests.post( - "http://supervisor/auth", headers=headers, data=data, timeout=30 + "http://supervisor/auth", headers=headers, json=data, timeout=30 ) if req.status_code == 200: self.set_secure_cookie("authenticated", cookie_authenticated_yes) From f77118a90cf7e0cad80fd226b59db0a1ca7f7408 Mon Sep 17 00:00:00 2001 From: Philippe FOUQUET Date: Thu, 8 Sep 2022 01:40:27 +0200 Subject: [PATCH 50/66] Add support to tm1621 display (#3737) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/tm1621/__init__.py | 1 + esphome/components/tm1621/display.py | 47 +++++ esphome/components/tm1621/tm1621.cpp | 283 ++++++++++++++++++++++++++ esphome/components/tm1621/tm1621.h | 74 +++++++ esphome/const.py | 2 + tests/test1.yaml | 13 ++ 7 files changed, 421 insertions(+) create mode 100644 esphome/components/tm1621/__init__.py create mode 100644 esphome/components/tm1621/display.py create mode 100644 esphome/components/tm1621/tm1621.cpp create mode 100644 esphome/components/tm1621/tm1621.h diff --git a/CODEOWNERS b/CODEOWNERS index ae112b3330..f8efe79c81 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -225,6 +225,7 @@ esphome/components/teleinfo/* @0hax esphome/components/thermostat/* @kbx81 esphome/components/time/* @OttoWinter esphome/components/tlc5947/* @rnauber +esphome/components/tm1621/* @Philippe12 esphome/components/tm1637/* @glmnet esphome/components/tmp102/* @timsavage esphome/components/tmp117/* @Azimath diff --git a/esphome/components/tm1621/__init__.py b/esphome/components/tm1621/__init__.py new file mode 100644 index 0000000000..2e88d4f366 --- /dev/null +++ b/esphome/components/tm1621/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@Philippe12"] diff --git a/esphome/components/tm1621/display.py b/esphome/components/tm1621/display.py new file mode 100644 index 0000000000..edbc5f6928 --- /dev/null +++ b/esphome/components/tm1621/display.py @@ -0,0 +1,47 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.components import display +from esphome.const import ( + CONF_DATA_PIN, + CONF_CS_PIN, + CONF_ID, + CONF_LAMBDA, + CONF_READ_PIN, + CONF_WRITE_PIN, +) + +tm1621_ns = cg.esphome_ns.namespace("tm1621") +TM1621Display = tm1621_ns.class_("TM1621Display", cg.PollingComponent) +TM1621DisplayRef = TM1621Display.operator("ref") + +CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TM1621Display), + cv.Required(CONF_CS_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_READ_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_WRITE_PIN): pins.gpio_output_pin_schema, + } +).extend(cv.polling_component_schema("1s")) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await display.register_display(var, config) + + cs = await cg.gpio_pin_expression(config[CONF_CS_PIN]) + cg.add(var.set_cs_pin(cs)) + data = await cg.gpio_pin_expression(config[CONF_DATA_PIN]) + cg.add(var.set_data_pin(data)) + read = await cg.gpio_pin_expression(config[CONF_READ_PIN]) + cg.add(var.set_read_pin(read)) + write = await cg.gpio_pin_expression(config[CONF_WRITE_PIN]) + cg.add(var.set_write_pin(write)) + + if CONF_LAMBDA in config: + lambda_ = await cg.process_lambda( + config[CONF_LAMBDA], [(TM1621DisplayRef, "it")], return_type=cg.void + ) + cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/tm1621/tm1621.cpp b/esphome/components/tm1621/tm1621.cpp new file mode 100644 index 0000000000..ebaa5a3457 --- /dev/null +++ b/esphome/components/tm1621/tm1621.cpp @@ -0,0 +1,283 @@ +#include "tm1621.h" +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace tm1621 { + +static const char *const TAG = "tm1621"; + +const uint8_t TM1621_PULSE_WIDTH = 10; // microseconds (Sonoff = 100) + +const uint8_t TM1621_SYS_EN = 0x01; // 0b00000001 +const uint8_t TM1621_LCD_ON = 0x03; // 0b00000011 +const uint8_t TM1621_TIMER_DIS = 0x04; // 0b00000100 +const uint8_t TM1621_WDT_DIS = 0x05; // 0b00000101 +const uint8_t TM1621_TONE_OFF = 0x08; // 0b00001000 +const uint8_t TM1621_BIAS = 0x29; // 0b00101001 = LCD 1/3 bias 4 commons option +const uint8_t TM1621_IRQ_DIS = 0x80; // 0b100x0xxx + +enum Tm1621Device { TM1621_USER, TM1621_POWR316D, TM1621_THR316D }; + +const uint8_t TM1621_COMMANDS[] = {TM1621_SYS_EN, TM1621_LCD_ON, TM1621_BIAS, TM1621_TIMER_DIS, + TM1621_WDT_DIS, TM1621_TONE_OFF, TM1621_IRQ_DIS}; + +const char TM1621_KCHAR[] PROGMEM = {"0|1|2|3|4|5|6|7|8|9|-| "}; +// 0 1 2 3 4 5 6 7 8 9 - off +const uint8_t TM1621_DIGIT_ROW[2][12] = {{0x5F, 0x50, 0x3D, 0x79, 0x72, 0x6B, 0x6F, 0x51, 0x7F, 0x7B, 0x20, 0x00}, + {0xF5, 0x05, 0xB6, 0x97, 0x47, 0xD3, 0xF3, 0x85, 0xF7, 0xD7, 0x02, 0x00}}; + +void TM1621Display::setup() { + ESP_LOGCONFIG(TAG, "Setting up TM1621..."); + + this->cs_pin_->setup(); // OUTPUT + this->cs_pin_->digital_write(true); + this->data_pin_->setup(); // OUTPUT + this->data_pin_->digital_write(true); + this->read_pin_->setup(); // OUTPUT + this->read_pin_->digital_write(true); + this->write_pin_->setup(); // OUTPUT + this->write_pin_->digital_write(true); + + this->state_ = 100; + + this->cs_pin_->digital_write(false); + delayMicroseconds(80); + this->read_pin_->digital_write(false); + delayMicroseconds(15); + this->write_pin_->digital_write(false); + delayMicroseconds(25); + this->data_pin_->digital_write(false); + delayMicroseconds(TM1621_PULSE_WIDTH); + this->data_pin_->digital_write(true); + + for (uint8_t tm1621_command : TM1621_COMMANDS) { + this->send_command_(tm1621_command); + } + + this->send_address_(0x00); + for (uint32_t segment = 0; segment < 16; segment++) { + this->send_common_(0); + } + this->stop_(); + + snprintf(this->row_[0], sizeof(this->row_[0]), "----"); + snprintf(this->row_[1], sizeof(this->row_[1]), "----"); + + this->display(); +} +void TM1621Display::dump_config() { + ESP_LOGCONFIG(TAG, "TM1621:"); + LOG_PIN(" CS Pin: ", this->cs_pin_); + LOG_PIN(" DATA Pin: ", this->data_pin_); + LOG_PIN(" READ Pin: ", this->read_pin_); + LOG_PIN(" WRITE Pin: ", this->write_pin_); + LOG_UPDATE_INTERVAL(this); +} + +void TM1621Display::update() { + // memset(this->row, 0, sizeof(this->row)); + if (this->writer_.has_value()) + (*this->writer_)(*this); + this->display(); +} + +float TM1621Display::get_setup_priority() const { return setup_priority::PROCESSOR; } +void TM1621Display::bit_delay_() { delayMicroseconds(100); } + +void TM1621Display::stop_() { + this->cs_pin_->digital_write(true); // Stop command sequence + delayMicroseconds(TM1621_PULSE_WIDTH / 2); + this->data_pin_->digital_write(true); // Reset data +} + +void TM1621Display::display() { + // Tm1621.row[x] = "text", "----", " " or a number with one decimal like "0.4", "237.5", "123456.7" + // "123456.7" will be shown as "9999" being a four digit overflow + + // AddLog(LOG_LEVEL_DEBUG, PSTR("TM1: Row1 '%s', Row2 '%s'"), Tm1621.row[0], Tm1621.row[1]); + + uint8_t buffer[8] = {0}; // TM1621 16-segment 4-bit common buffer + char row[4]; + for (uint32_t j = 0; j < 2; j++) { + // 0.4V => " 04", 0.0A => " ", 1234.5V => "1234" + uint32_t len = strlen(this->row_[j]); + char *dp = nullptr; // Expect number larger than "123" + int row_idx = len - 3; // "1234.5" + if (len <= 5) { // "----", " ", "0.4", "237.5" + dp = strchr(this->row_[j], '.'); + row_idx = len - 1; + } else if (len > 6) { // "12345.6" + snprintf(this->row_[j], sizeof(this->row_[j]), "9999"); + row_idx = 3; + } + row[3] = (row_idx >= 0) ? this->row_[j][row_idx--] : ' '; + if ((row_idx >= 0) && dp) { + row_idx--; + } + row[2] = (row_idx >= 0) ? this->row_[j][row_idx--] : ' '; + row[1] = (row_idx >= 0) ? this->row_[j][row_idx--] : ' '; + row[0] = (row_idx >= 0) ? this->row_[j][row_idx--] : ' '; + + // AddLog(LOG_LEVEL_DEBUG, PSTR("TM1: Dump%d %4_H"), j +1, row); + + char command[10]; + char needle[2] = {0}; + for (uint32_t i = 0; i < 4; i++) { + needle[0] = row[i]; + int index = this->get_command_code_(command, sizeof(command), (const char *) needle, TM1621_KCHAR); + if (-1 == index) { + index = 11; + } + uint32_t bidx = (0 == j) ? i : 7 - i; + buffer[bidx] = TM1621_DIGIT_ROW[j][index]; + } + if (dp) { + if (0 == j) { + buffer[2] |= 0x80; // Row 1 decimal point + } else { + buffer[5] |= 0x08; // Row 2 decimal point + } + } + } + + if (this->fahrenheit_) { + buffer[1] |= 0x80; + } + if (this->celsius_) { + buffer[3] |= 0x80; + } + if (this->kwh_) { + buffer[4] |= 0x08; + } + if (this->humidity_) { + buffer[6] |= 0x08; + } + if (this->voltage_) { + buffer[7] |= 0x08; + } + + // AddLog(LOG_LEVEL_DEBUG, PSTR("TM1: Dump3 %8_H"), buffer); + + this->send_address_(0x10); // Sonoff only uses the upper 16 Segments + for (uint8_t i : buffer) { + this->send_common_(i); + } + this->stop_(); +} + +bool TM1621Display::send_command_(uint16_t command) { + uint16_t full_command = (0x0400 | command) << 5; // 0b100cccccccc00000 + this->cs_pin_->digital_write(false); // Start command sequence + delayMicroseconds(TM1621_PULSE_WIDTH / 2); + for (uint32_t i = 0; i < 12; i++) { + this->write_pin_->digital_write(false); // Start write sequence + if (full_command & 0x8000) { + this->data_pin_->digital_write(true); // Set data + } else { + this->data_pin_->digital_write(false); // Set data + } + delayMicroseconds(TM1621_PULSE_WIDTH); + this->write_pin_->digital_write(true); // Read data + delayMicroseconds(TM1621_PULSE_WIDTH); + full_command <<= 1; + } + this->stop_(); + return true; +} + +bool TM1621Display::send_common_(uint8_t common) { + for (uint32_t i = 0; i < 8; i++) { + this->write_pin_->digital_write(false); // Start write sequence + if (common & 1) { + this->data_pin_->digital_write(true); // Set data + } else { + this->data_pin_->digital_write(false); // Set data + } + delayMicroseconds(TM1621_PULSE_WIDTH); + this->write_pin_->digital_write(true); // Read data + delayMicroseconds(TM1621_PULSE_WIDTH); + common >>= 1; + } + return true; +} + +bool TM1621Display::send_address_(uint16_t address) { + uint16_t full_address = (address | 0x0140) << 7; // 0b101aaaaaa0000000 + this->cs_pin_->digital_write(false); // Start command sequence + delayMicroseconds(TM1621_PULSE_WIDTH / 2); + for (uint32_t i = 0; i < 9; i++) { + this->write_pin_->digital_write(false); // Start write sequence + if (full_address & 0x8000) { + this->data_pin_->digital_write(true); // Set data + } else { + this->data_pin_->digital_write(false); // Set data + } + delayMicroseconds(TM1621_PULSE_WIDTH); + this->write_pin_->digital_write(true); // Read data + delayMicroseconds(TM1621_PULSE_WIDTH); + full_address <<= 1; + } + return true; +} + +uint8_t TM1621Display::print(uint8_t start_pos, const char *str) { + // ESP_LOGD(TAG, "Print at %d: %s", start_pos, str); + return snprintf(this->row_[start_pos], sizeof(this->row_[start_pos]), "%s", str); +} +uint8_t TM1621Display::print(const char *str) { return this->print(0, str); } +uint8_t TM1621Display::printf(uint8_t pos, const char *format, ...) { + va_list arg; + va_start(arg, format); + char buffer[64]; + int ret = vsnprintf(buffer, sizeof(buffer), format, arg); + va_end(arg); + if (ret > 0) + return this->print(pos, buffer); + return 0; +} +uint8_t TM1621Display::printf(const char *format, ...) { + va_list arg; + va_start(arg, format); + char buffer[64]; + int ret = vsnprintf(buffer, sizeof(buffer), format, arg); + va_end(arg); + if (ret > 0) + return this->print(buffer); + return 0; +} + +int TM1621Display::get_command_code_(char *destination, size_t destination_size, const char *needle, + const char *haystack) { + // Returns -1 of not found + // Returns index and command if found + int result = -1; + const char *read = haystack; + char *write = destination; + + while (true) { + result++; + size_t size = destination_size - 1; + write = destination; + char ch = '.'; + while ((ch != '\0') && (ch != '|')) { + ch = *(read++); + if (size && (ch != '|')) { + *write++ = ch; + size--; + } + } + *write = '\0'; + if (!strcasecmp(needle, destination)) { + break; + } + if (0 == ch) { + result = -1; + break; + } + } + return result; +} +} // namespace tm1621 +} // namespace esphome diff --git a/esphome/components/tm1621/tm1621.h b/esphome/components/tm1621/tm1621.h new file mode 100644 index 0000000000..b9f330e96e --- /dev/null +++ b/esphome/components/tm1621/tm1621.h @@ -0,0 +1,74 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/defines.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace tm1621 { + +class TM1621Display; + +using tm1621_writer_t = std::function; + +class TM1621Display : public PollingComponent { + public: + void set_writer(tm1621_writer_t &&writer) { this->writer_ = writer; } + + void setup() override; + + void dump_config() override; + + void set_cs_pin(GPIOPin *pin) { cs_pin_ = pin; } + void set_data_pin(GPIOPin *pin) { data_pin_ = pin; } + void set_read_pin(GPIOPin *pin) { read_pin_ = pin; } + void set_write_pin(GPIOPin *pin) { write_pin_ = pin; } + + void display_celsius(bool d) { celsius_ = d; } + void display_fahrenheit(bool d) { fahrenheit_ = d; } + void display_humidity(bool d) { humidity_ = d; } + void display_voltage(bool d) { voltage_ = d; } + void display_kwh(bool d) { kwh_ = d; } + + float get_setup_priority() const override; + + void update() override; + + /// Evaluate the printf-format and print the result at the given position. + uint8_t printf(uint8_t pos, const char *format, ...) __attribute__((format(printf, 3, 4))); + /// Evaluate the printf-format and print the result at position 0. + uint8_t printf(const char *format, ...) __attribute__((format(printf, 2, 3))); + + /// Print `str` at the given position. + uint8_t print(uint8_t pos, const char *str); + /// Print `str` at position 0. + uint8_t print(const char *str); + + void display(); + + protected: + void bit_delay_(); + void setup_pins_(); + bool send_command_(uint16_t command); + bool send_common_(uint8_t common); + bool send_address_(uint16_t address); + void stop_(); + int get_command_code_(char *destination, size_t destination_size, const char *needle, const char *haystack); + + GPIOPin *data_pin_; + GPIOPin *cs_pin_; + GPIOPin *read_pin_; + GPIOPin *write_pin_; + optional writer_{}; + char row_[2][12]; + uint8_t state_; + uint8_t device_; + bool celsius_; + bool fahrenheit_; + bool humidity_; + bool voltage_; + bool kwh_; +}; + +} // namespace tm1621 +} // namespace esphome diff --git a/esphome/const.py b/esphome/const.py index 8245ed26f7..adbd20c620 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -558,6 +558,7 @@ CONF_RAW_DATA_ID = "raw_data_id" CONF_RC_CODE_1 = "rc_code_1" CONF_RC_CODE_2 = "rc_code_2" CONF_REACTIVE_POWER = "reactive_power" +CONF_READ_PIN = "read_pin" CONF_REBOOT_TIMEOUT = "reboot_timeout" CONF_RECEIVE_TIMEOUT = "receive_timeout" CONF_RED = "red" @@ -769,6 +770,7 @@ CONF_WILL_MESSAGE = "will_message" CONF_WIND_DIRECTION_DEGREES = "wind_direction_degrees" CONF_WIND_SPEED = "wind_speed" CONF_WINDOW_SIZE = "window_size" +CONF_WRITE_PIN = "write_pin" CONF_X_GRID = "x_grid" CONF_Y_GRID = "y_grid" CONF_ZERO = "zero" diff --git a/tests/test1.yaml b/tests/test1.yaml index 274b25548b..20209923aa 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -547,8 +547,10 @@ sensor: - platform: dht pin: GPIO26 temperature: + id: dht_temperature name: Living Room Temperature 3 humidity: + id: dht_humidity name: Living Room Humidity 3 model: AM2302 update_interval: 15s @@ -2514,6 +2516,17 @@ display: it.print_sad(true); it.print_bracket(true); it.print_battery(true); + - platform: tm1621 + id: tm1621_display + cs_pin: GPIO17 + data_pin: GPIO5 + read_pin: GPIO23 + write_pin: GPIO18 + lambda: |- + it.printf(0, "%.1f", id(dht_temperature).state); + it.display_celsius(true); + it.printf(1, "%.1f", id(dht_humidity).state); + it.display_humidity(true); tm1651: id: tm1651_battery From e3f2562047847cd849ccd05674929bf2c2238f6f Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 8 Sep 2022 12:30:07 +0200 Subject: [PATCH 51/66] u-fire EC sensor (#3774) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/ufire_ec/__init__.py | 1 + esphome/components/ufire_ec/sensor.py | 126 +++++++++++++++++++++++ esphome/components/ufire_ec/ufire_ec.cpp | 119 +++++++++++++++++++++ esphome/components/ufire_ec/ufire_ec.h | 87 ++++++++++++++++ esphome/const.py | 2 + tests/test2.yaml | 7 ++ tests/test4.yaml | 8 ++ 8 files changed, 351 insertions(+) create mode 100644 esphome/components/ufire_ec/__init__.py create mode 100644 esphome/components/ufire_ec/sensor.py create mode 100644 esphome/components/ufire_ec/ufire_ec.cpp create mode 100644 esphome/components/ufire_ec/ufire_ec.h diff --git a/CODEOWNERS b/CODEOWNERS index f8efe79c81..ff497c35ec 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -241,6 +241,7 @@ esphome/components/tuya/sensor/* @jesserockz esphome/components/tuya/switch/* @jesserockz esphome/components/tuya/text_sensor/* @dentra esphome/components/uart/* @esphome/core +esphome/components/ufire_ec/* @pvizeli esphome/components/ultrasonic/* @OttoWinter esphome/components/version/* @esphome/core esphome/components/wake_on_lan/* @willwill2will54 diff --git a/esphome/components/ufire_ec/__init__.py b/esphome/components/ufire_ec/__init__.py new file mode 100644 index 0000000000..08f36c7934 --- /dev/null +++ b/esphome/components/ufire_ec/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@pvizeli"] diff --git a/esphome/components/ufire_ec/sensor.py b/esphome/components/ufire_ec/sensor.py new file mode 100644 index 0000000000..9602d0c2d0 --- /dev/null +++ b/esphome/components/ufire_ec/sensor.py @@ -0,0 +1,126 @@ +import esphome.codegen as cg +from esphome import automation +import esphome.config_validation as cv +from esphome.components import i2c, sensor +from esphome.const import ( + CONF_ID, + CONF_EC, + CONF_TEMPERATURE, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + UNIT_MILLISIEMENS_PER_CENTIMETER, +) + +DEPENDENCIES = ["i2c"] + +CONF_SOLUTION = "solution" +CONF_TEMPERATURE_SENSOR = "temperature_sensor" +CONF_TEMPERATURE_COMPENSATION = "temperature_compensation" +CONF_TEMPERATURE_COEFFICIENT = "temperature_coefficient" + +ufire_ec_ns = cg.esphome_ns.namespace("ufire_ec") +UFireECComponent = ufire_ec_ns.class_( + "UFireECComponent", cg.PollingComponent, i2c.I2CDevice +) + +# Actions +UFireECCalibrateProbeAction = ufire_ec_ns.class_( + "UFireECCalibrateProbeAction", automation.Action +) +UFireECResetAction = ufire_ec_ns.class_("UFireECResetAction", automation.Action) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(UFireECComponent), + cv.Exclusive(CONF_TEMPERATURE, "temperature"): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + accuracy_decimals=1, + ), + cv.Optional(CONF_EC): sensor.sensor_schema( + unit_of_measurement=UNIT_MILLISIEMENS_PER_CENTIMETER, + icon=ICON_EMPTY, + device_class=DEVICE_CLASS_EMPTY, + state_class=STATE_CLASS_MEASUREMENT, + accuracy_decimals=1, + ), + cv.Exclusive(CONF_TEMPERATURE_SENSOR, "temperature"): cv.use_id( + sensor.Sensor + ), + cv.Optional(CONF_TEMPERATURE_COMPENSATION, default=21.0): cv.temperature, + cv.Optional(CONF_TEMPERATURE_COEFFICIENT, default=0.019): cv.float_range( + min=0.01, max=0.04 + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x3C)) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + cg.add(var.set_temperature_compensation(config[CONF_TEMPERATURE_COMPENSATION])) + cg.add(var.set_temperature_coefficient(config[CONF_TEMPERATURE_COEFFICIENT])) + + if CONF_TEMPERATURE in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE]) + cg.add(var.set_temperature_sensor(sens)) + + if CONF_EC in config: + sens = await sensor.new_sensor(config[CONF_EC]) + cg.add(var.set_ec_sensor(sens)) + + if CONF_TEMPERATURE_SENSOR in config: + sens = await cg.get_variable(config[CONF_TEMPERATURE_SENSOR]) + cg.add(var.set_temperature_sensor_external(sens)) + + await i2c.register_i2c_device(var, config) + + +UFIRE_EC_CALIBRATE_PROBE_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(UFireECComponent), + cv.Required(CONF_SOLUTION): cv.templatable(float), + cv.Required(CONF_TEMPERATURE): cv.templatable(cv.temperature), + } +) + + +@automation.register_action( + "ufire_ec.calibrate_probe", + UFireECCalibrateProbeAction, + UFIRE_EC_CALIBRATE_PROBE_SCHEMA, +) +async def ufire_ec_calibrate_probe_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + solution_ = await cg.templatable(config[CONF_SOLUTION], args, float) + temperature_ = await cg.templatable(config[CONF_TEMPERATURE], args, float) + cg.add(var.set_solution(solution_)) + cg.add(var.set_temperature(temperature_)) + return var + + +UFIRE_EC_RESET_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(UFireECComponent), + } +) + + +@automation.register_action( + "ufire_ec.reset", + UFireECResetAction, + UFIRE_EC_RESET_SCHEMA, +) +async def ufire_ec_reset_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + return var diff --git a/esphome/components/ufire_ec/ufire_ec.cpp b/esphome/components/ufire_ec/ufire_ec.cpp new file mode 100644 index 0000000000..d7ed890e21 --- /dev/null +++ b/esphome/components/ufire_ec/ufire_ec.cpp @@ -0,0 +1,119 @@ +#include "esphome/core/log.h" +#include "ufire_ec.h" + +namespace esphome { +namespace ufire_ec { + +static const char *const TAG = "ufire_ec"; + +void UFireECComponent::setup() { + ESP_LOGCONFIG(TAG, "Setting up uFire_ec..."); + + uint8_t version; + if (!this->read_byte(REGISTER_VERSION, &version) && version != 0xFF) { + this->mark_failed(); + this->status_set_error(); + return; + } + ESP_LOGI(TAG, "Found ufire_ec board version 0x%02X", version); + + // Write option for temperature adjustments + uint8_t config; + this->read_byte(REGISTER_CONFIG, &config); + if (this->temperature_sensor_ == nullptr && this->temperature_sensor_external_ == nullptr) { + config &= ~CONFIG_TEMP_COMPENSATION; + } else { + config |= CONFIG_TEMP_COMPENSATION; + } + this->write_byte(REGISTER_CONFIG, config); + + // Update temperature compensation + this->set_compensation_(this->temperature_compensation_); + this->set_coefficient_(this->temperature_coefficient_); +} + +void UFireECComponent::update() { + int wait = 0; + + if (this->temperature_sensor_ != nullptr) { + this->write_byte(REGISTER_TASK, COMMAND_MEASURE_TEMP); + wait += 750; + } else if (this->temperature_sensor_external_ != nullptr) { + this->set_temperature_(this->temperature_sensor_external_->state); + } + + if (this->ec_sensor_ != nullptr) { + this->write_byte(REGISTER_TASK, COMMAND_MEASURE_EC); + wait += 750; + } + + if (wait > 0) { + this->set_timeout("data", wait, [this]() { this->update_internal_(); }); + } +} + +void UFireECComponent::update_internal_() { + if (this->temperature_sensor_ != nullptr) + this->temperature_sensor_->publish_state(this->measure_temperature_()); + if (this->ec_sensor_ != nullptr) + this->ec_sensor_->publish_state(this->measure_ms_()); +} + +float UFireECComponent::measure_temperature_() { return this->read_data_(REGISTER_TEMP); } + +float UFireECComponent::measure_ms_() { return this->read_data_(REGISTER_MS); } + +void UFireECComponent::set_solution_(float solution, float temperature) { + solution /= (1 - (this->temperature_coefficient_ * (temperature - 25))); + this->write_data_(REGISTER_SOLUTION, solution); +} + +void UFireECComponent::set_compensation_(float temperature) { this->write_data_(REGISTER_COMPENSATION, temperature); } + +void UFireECComponent::set_coefficient_(float coefficient) { this->write_data_(REGISTER_COEFFICENT, coefficient); } + +void UFireECComponent::set_temperature_(float temperature) { this->write_data_(REGISTER_TEMP, temperature); } + +void UFireECComponent::calibrate_probe(float solution, float temperature) { + this->set_solution_(solution, temperature); + this->write_byte(REGISTER_TASK, COMMAND_CALIBRATE_PROBE); +} + +void UFireECComponent::reset_board() { this->write_data_(REGISTER_CALIBRATE_OFFSET, NAN); } + +float UFireECComponent::read_data_(uint8_t reg) { + float f; + uint8_t temp[4]; + + this->write(®, 1); + delay(10); + + for (uint8_t i = 0; i < 4; i++) { + this->read_bytes_raw(temp + i, 1); + } + memcpy(&f, temp, sizeof(f)); + + return f; +} + +void UFireECComponent::write_data_(uint8_t reg, float data) { + uint8_t temp[4]; + + memcpy(temp, &data, sizeof(data)); + this->write_bytes(reg, temp, 4); + delay(10); +} + +void UFireECComponent::dump_config() { + ESP_LOGCONFIG(TAG, "uFire-EC"); + LOG_I2C_DEVICE(this) + LOG_UPDATE_INTERVAL(this) + LOG_SENSOR(" ", "EC Sensor", this->ec_sensor_) + LOG_SENSOR(" ", "Temperature Sensor", this->temperature_sensor_) + LOG_SENSOR(" ", "Temperature Sensor external", this->temperature_sensor_external_) + ESP_LOGCONFIG(TAG, " Temperature Compensation: %f", this->temperature_compensation_); + ESP_LOGCONFIG(TAG, " Temperature Coefficient: %f", this->temperature_coefficient_); +} + +} // namespace ufire_ec +} // namespace esphome diff --git a/esphome/components/ufire_ec/ufire_ec.h b/esphome/components/ufire_ec/ufire_ec.h new file mode 100644 index 0000000000..3d436555a2 --- /dev/null +++ b/esphome/components/ufire_ec/ufire_ec.h @@ -0,0 +1,87 @@ +#pragma once + +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace ufire_ec { + +static const uint8_t CONFIG_TEMP_COMPENSATION = 0x02; + +static const uint8_t REGISTER_VERSION = 0; +static const uint8_t REGISTER_MS = 1; +static const uint8_t REGISTER_TEMP = 5; +static const uint8_t REGISTER_SOLUTION = 9; +static const uint8_t REGISTER_COEFFICENT = 13; +static const uint8_t REGISTER_CALIBRATE_OFFSET = 33; +static const uint8_t REGISTER_COMPENSATION = 45; +static const uint8_t REGISTER_CONFIG = 54; +static const uint8_t REGISTER_TASK = 55; + +static const uint8_t COMMAND_CALIBRATE_PROBE = 20; +static const uint8_t COMMAND_MEASURE_TEMP = 40; +static const uint8_t COMMAND_MEASURE_EC = 80; + +class UFireECComponent : public PollingComponent, public i2c::I2CDevice { + public: + void setup() override; + void update() override; + void dump_config() override; + + void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; } + void set_temperature_sensor_external(sensor::Sensor *temperature_sensor) { + this->temperature_sensor_external_ = temperature_sensor; + } + void set_ec_sensor(sensor::Sensor *ec_sensor) { this->ec_sensor_ = ec_sensor; } + void set_temperature_compensation(float compensation) { this->temperature_compensation_ = compensation; } + void set_temperature_coefficient(float coefficient) { this->temperature_coefficient_ = coefficient; } + void calibrate_probe(float solution, float temperature); + void reset_board(); + + protected: + float measure_temperature_(); + float measure_ms_(); + void set_solution_(float solution, float temperature); + void set_compensation_(float temperature); + void set_coefficient_(float coefficient); + void set_temperature_(float temperature); + float read_data_(uint8_t reg); + void write_data_(uint8_t reg, float data); + void update_internal_(); + + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *temperature_sensor_external_{nullptr}; + sensor::Sensor *ec_sensor_{nullptr}; + float temperature_compensation_{0.0}; + float temperature_coefficient_{0.0}; +}; + +template class UFireECCalibrateProbeAction : public Action { + public: + UFireECCalibrateProbeAction(UFireECComponent *parent) : parent_(parent) {} + TEMPLATABLE_VALUE(float, solution) + TEMPLATABLE_VALUE(float, temperature) + + void play(Ts... x) override { + this->parent_->calibrate_probe(this->solution_.value(x...), this->temperature_.value(x...)); + } + + protected: + UFireECComponent *parent_; +}; + +template class UFireECResetAction : public Action { + public: + UFireECResetAction(UFireECComponent *parent) : parent_(parent) {} + + void play(Ts... x) override { this->parent_->reset_board(); } + + protected: + UFireECComponent *parent_; +}; + +} // namespace ufire_ec +} // namespace esphome diff --git a/esphome/const.py b/esphome/const.py index adbd20c620..51c07ab666 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -192,6 +192,7 @@ CONF_DUMMY_RECEIVER_ID = "dummy_receiver_id" CONF_DUMP = "dump" CONF_DURATION = "duration" CONF_EAP = "eap" +CONF_EC = "ec" CONF_ECHO_PIN = "echo_pin" CONF_ECO2 = "eco2" CONF_EFFECT = "effect" @@ -871,6 +872,7 @@ UNIT_MICROSIEMENS_PER_CENTIMETER = "µS/cm" UNIT_MICROTESLA = "µT" UNIT_MILLIGRAMS_PER_CUBIC_METER = "mg/m³" UNIT_MILLISECOND = "ms" +UNIT_MILLISIEMENS_PER_CENTIMETER = "mS/cm" UNIT_MINUTE = "min" UNIT_OHM = "Ω" UNIT_PARTS_PER_BILLION = "ppb" diff --git a/tests/test2.yaml b/tests/test2.yaml index d01a0a742a..f8ed04d389 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -369,6 +369,13 @@ sensor: name: Propane test distance battery_level: name: Propane test battery level + - platform: ufire_ec + id: ufire_ec_board + ec: + name: Ufire EC + temperature_sensor: ha_hello_world_temperature + temperature_compensation: 20.0 + temperature_coefficient: 0.019 time: - platform: homeassistant diff --git a/tests/test4.yaml b/tests/test4.yaml index d6857de7cd..d79b421cf8 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -243,6 +243,14 @@ sensor: - platform: copy source_id: mcp_sensor name: MCP binary sensor copy + - platform: ufire_ec + id: ufire_ec_board + temperature: + name: Ufire Temperature + ec: + name: Ufire EC + temperature_compensation: 20.0 + temperature_coefficient: 0.019 # # platform sensor.apds9960 requires component apds9960 From d66b2a1778c0add96b353328f67d6f514688751d Mon Sep 17 00:00:00 2001 From: Keilin Bickar Date: Fri, 9 Sep 2022 01:18:02 -0400 Subject: [PATCH 52/66] Add support for MPL3115A2 Pressure/Altitude and Temperature Sensor (#3371) * Add support for mpl3115a2 * Add codeowner * Linter/test updates * Minor changes * Made pressure/altitude exclusive Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/mpl3115a2/__init__.py | 0 esphome/components/mpl3115a2/mpl3115a2.cpp | 99 +++++++++++++++++++ esphome/components/mpl3115a2/mpl3115a2.h | 108 +++++++++++++++++++++ esphome/components/mpl3115a2/sensor.py | 75 ++++++++++++++ tests/test1.yaml | 7 ++ 6 files changed, 290 insertions(+) create mode 100644 esphome/components/mpl3115a2/__init__.py create mode 100644 esphome/components/mpl3115a2/mpl3115a2.cpp create mode 100644 esphome/components/mpl3115a2/mpl3115a2.h create mode 100644 esphome/components/mpl3115a2/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index ff497c35ec..0441a6bfcd 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -143,6 +143,7 @@ esphome/components/modbus_controller/switch/* @martgras esphome/components/modbus_controller/text_sensor/* @martgras esphome/components/mopeka_ble/* @spbrogan esphome/components/mopeka_pro_check/* @spbrogan +esphome/components/mpl3115a2/* @kbickar esphome/components/mpu6886/* @fabaff esphome/components/network/* @esphome/core esphome/components/nextion/* @senexcrenshaw diff --git a/esphome/components/mpl3115a2/__init__.py b/esphome/components/mpl3115a2/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/mpl3115a2/mpl3115a2.cpp b/esphome/components/mpl3115a2/mpl3115a2.cpp new file mode 100644 index 0000000000..f1e553e107 --- /dev/null +++ b/esphome/components/mpl3115a2/mpl3115a2.cpp @@ -0,0 +1,99 @@ +#include "mpl3115a2.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace mpl3115a2 { + +static const char *const TAG = "mpl3115a2"; + +void MPL3115A2Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up MPL3115A2..."); + + uint8_t whoami = 0xFF; + if (!this->read_byte(MPL3115A2_WHOAMI, &whoami, false)) { + this->error_code_ = COMMUNICATION_FAILED; + this->mark_failed(); + return; + } + if (whoami != 0xC4) { + this->error_code_ = WRONG_ID; + this->mark_failed(); + return; + } + + // reset + this->write_byte(MPL3115A2_CTRL_REG1, MPL3115A2_CTRL_REG1_RST); + delay(15); + + // enable data ready events for pressure/altitude and temperature + this->write_byte(MPL3115A2_PT_DATA_CFG, + MPL3115A2_PT_DATA_CFG_TDEFE | MPL3115A2_PT_DATA_CFG_PDEFE | MPL3115A2_PT_DATA_CFG_DREM); +} + +void MPL3115A2Component::dump_config() { + ESP_LOGCONFIG(TAG, "MPL3115A2:"); + LOG_I2C_DEVICE(this); + if (this->is_failed()) { + switch (this->error_code_) { + case COMMUNICATION_FAILED: + ESP_LOGE(TAG, "Communication with MPL3115A2 failed!"); + break; + case WRONG_ID: + ESP_LOGE(TAG, "MPL3115A2 has invalid id"); + break; + default: + ESP_LOGE(TAG, "Setting up MPL3115A2 registers failed!"); + break; + } + } + LOG_UPDATE_INTERVAL(this); + LOG_SENSOR(" ", "Temperature", this->temperature_); + LOG_SENSOR(" ", "Pressure", this->pressure_); + LOG_SENSOR(" ", "Altitude", this->altitude_); +} + +void MPL3115A2Component::update() { + uint8_t mode = MPL3115A2_CTRL_REG1_OS128; + this->write_byte(MPL3115A2_CTRL_REG1, mode, true); + // Trigger a new reading + mode |= MPL3115A2_CTRL_REG1_OST; + if (this->altitude_ != nullptr) + mode |= MPL3115A2_CTRL_REG1_ALT; + this->write_byte(MPL3115A2_CTRL_REG1, mode, true); + + // Wait until status shows reading available + uint8_t status = 0; + if (!this->read_byte(MPL3115A2_REGISTER_STATUS, &status, false) || (status & MPL3115A2_REGISTER_STATUS_PDR) == 0) { + delay(10); + if (!this->read_byte(MPL3115A2_REGISTER_STATUS, &status, false) || (status & MPL3115A2_REGISTER_STATUS_PDR) == 0) { + return; + } + } + + uint8_t buffer[5] = {0, 0, 0, 0, 0}; + this->read_register(MPL3115A2_REGISTER_PRESSURE_MSB, buffer, 5, false); + + float altitude = 0, pressure = 0; + if (this->altitude_ != nullptr) { + int32_t alt = encode_uint32(buffer[0], buffer[1], buffer[2], 0); + altitude = float(alt) / 65536.0; + this->altitude_->publish_state(altitude); + } else { + uint32_t p = encode_uint32(0, buffer[0], buffer[1], buffer[2]); + pressure = float(p) / 6400.0; + if (this->pressure_ != nullptr) + this->pressure_->publish_state(pressure); + } + int16_t t = encode_uint16(buffer[3], buffer[4]); + float temperature = float(t) / 256.0; + if (this->temperature_ != nullptr) + this->temperature_->publish_state(temperature); + + ESP_LOGD(TAG, "Got Temperature=%.1f°C Altitude=%.1f Pressure=%.1f", temperature, altitude, pressure); + + this->status_clear_warning(); +} + +} // namespace mpl3115a2 +} // namespace esphome diff --git a/esphome/components/mpl3115a2/mpl3115a2.h b/esphome/components/mpl3115a2/mpl3115a2.h new file mode 100644 index 0000000000..00a6d90c52 --- /dev/null +++ b/esphome/components/mpl3115a2/mpl3115a2.h @@ -0,0 +1,108 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace mpl3115a2 { + +// enums from https://github.com/adafruit/Adafruit_MPL3115A2_Library/ +/** MPL3115A2 registers **/ +enum { + MPL3115A2_REGISTER_STATUS = (0x00), + + MPL3115A2_REGISTER_PRESSURE_MSB = (0x01), + MPL3115A2_REGISTER_PRESSURE_CSB = (0x02), + MPL3115A2_REGISTER_PRESSURE_LSB = (0x03), + + MPL3115A2_REGISTER_TEMP_MSB = (0x04), + MPL3115A2_REGISTER_TEMP_LSB = (0x05), + + MPL3115A2_REGISTER_DR_STATUS = (0x06), + + MPL3115A2_OUT_P_DELTA_MSB = (0x07), + MPL3115A2_OUT_P_DELTA_CSB = (0x08), + MPL3115A2_OUT_P_DELTA_LSB = (0x09), + + MPL3115A2_OUT_T_DELTA_MSB = (0x0A), + MPL3115A2_OUT_T_DELTA_LSB = (0x0B), + + MPL3115A2_WHOAMI = (0x0C), + + MPL3115A2_BAR_IN_MSB = (0x14), + MPL3115A2_BAR_IN_LSB = (0x15), +}; + +/** MPL3115A2 status register bits **/ +enum { + MPL3115A2_REGISTER_STATUS_TDR = 0x02, + MPL3115A2_REGISTER_STATUS_PDR = 0x04, + MPL3115A2_REGISTER_STATUS_PTDR = 0x08, +}; + +/** MPL3115A2 PT DATA register bits **/ +enum { + MPL3115A2_PT_DATA_CFG = 0x13, + MPL3115A2_PT_DATA_CFG_TDEFE = 0x01, + MPL3115A2_PT_DATA_CFG_PDEFE = 0x02, + MPL3115A2_PT_DATA_CFG_DREM = 0x04, +}; + +/** MPL3115A2 control registers **/ +enum { + + MPL3115A2_CTRL_REG1 = (0x26), + MPL3115A2_CTRL_REG2 = (0x27), + MPL3115A2_CTRL_REG3 = (0x28), + MPL3115A2_CTRL_REG4 = (0x29), + MPL3115A2_CTRL_REG5 = (0x2A), +}; + +/** MPL3115A2 control register bits **/ +enum { + MPL3115A2_CTRL_REG1_SBYB = 0x01, + MPL3115A2_CTRL_REG1_OST = 0x02, + MPL3115A2_CTRL_REG1_RST = 0x04, + MPL3115A2_CTRL_REG1_RAW = 0x40, + MPL3115A2_CTRL_REG1_ALT = 0x80, + MPL3115A2_CTRL_REG1_BAR = 0x00, +}; + +/** MPL3115A2 oversample values **/ +enum { + MPL3115A2_CTRL_REG1_OS1 = 0x00, + MPL3115A2_CTRL_REG1_OS2 = 0x08, + MPL3115A2_CTRL_REG1_OS4 = 0x10, + MPL3115A2_CTRL_REG1_OS8 = 0x18, + MPL3115A2_CTRL_REG1_OS16 = 0x20, + MPL3115A2_CTRL_REG1_OS32 = 0x28, + MPL3115A2_CTRL_REG1_OS64 = 0x30, + MPL3115A2_CTRL_REG1_OS128 = 0x38, +}; + +class MPL3115A2Component : public PollingComponent, public i2c::I2CDevice { + public: + void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; } + void set_altitude(sensor::Sensor *altitude) { altitude_ = altitude; } + void set_pressure(sensor::Sensor *pressure) { pressure_ = pressure; } + + void setup() override; + void dump_config() override; + void update() override; + + float get_setup_priority() const override { return setup_priority::DATA; } + + protected: + sensor::Sensor *temperature_{nullptr}; + sensor::Sensor *altitude_{nullptr}; + sensor::Sensor *pressure_{nullptr}; + enum ErrorCode { + NONE = 0, + COMMUNICATION_FAILED, + WRONG_ID, + } error_code_{NONE}; +}; + +} // namespace mpl3115a2 +} // namespace esphome diff --git a/esphome/components/mpl3115a2/sensor.py b/esphome/components/mpl3115a2/sensor.py new file mode 100644 index 0000000000..68ed0e08a8 --- /dev/null +++ b/esphome/components/mpl3115a2/sensor.py @@ -0,0 +1,75 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, sensor +from esphome.const import ( + CONF_ALTITUDE, + CONF_ID, + CONF_PRESSURE, + CONF_TEMPERATURE, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + UNIT_HECTOPASCAL, + UNIT_METER, +) + +CODEOWNERS = ["@kbickar"] +DEPENDENCIES = ["i2c"] + +mpl3115a2_ns = cg.esphome_ns.namespace("mpl3115a2") +MPL3115A2Component = mpl3115a2_ns.class_( + "MPL3115A2Component", cg.PollingComponent, i2c.I2CDevice +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MPL3115A2Component), + cv.Exclusive( + CONF_PRESSURE, + "pressure", + f"{CONF_PRESSURE} and {CONF_ALTITUDE} can't be used together", + ): sensor.sensor_schema( + unit_of_measurement=UNIT_HECTOPASCAL, + accuracy_decimals=1, + device_class=DEVICE_CLASS_PRESSURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Exclusive( + CONF_ALTITUDE, + "pressure", + f"{CONF_PRESSURE} and {CONF_ALTITUDE} can't be used together", + ): sensor.sensor_schema( + unit_of_measurement=UNIT_METER, + accuracy_decimals=1, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x60)) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + + if CONF_PRESSURE in config: + sens = await sensor.new_sensor(config[CONF_PRESSURE]) + cg.add(var.set_pressure(sens)) + elif CONF_ALTITUDE in config: + sens = await sensor.new_sensor(config[CONF_ALTITUDE]) + cg.add(var.set_altitude(sens)) + + if CONF_TEMPERATURE in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE]) + cg.add(var.set_temperature(sens)) diff --git a/tests/test1.yaml b/tests/test1.yaml index 20209923aa..e5e9754d74 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1167,6 +1167,13 @@ sensor: temperature: name: Max9611 Temp update_interval: 1s + - platform: mpl3115a2 + i2c_id: i2c_bus + temperature: + name: "MPL3115A2 Temperature" + pressure: + name: "MPL3115A2 Pressure" + update_interval: 10s esp32_touch: setup_mode: false From 8ba207fc7f9ba08cb87eb31c9a020ce3541a4d2f Mon Sep 17 00:00:00 2001 From: David Buezas Date: Sun, 11 Sep 2022 23:36:09 +0200 Subject: [PATCH 53/66] Add support for BL0942 voltage, current, energy and power Sensor (#3777) --- CODEOWNERS | 1 + esphome/components/bl0942/__init__.py | 1 + esphome/components/bl0942/bl0942.cpp | 121 ++++++++++++++++++++++++++ esphome/components/bl0942/bl0942.h | 68 +++++++++++++++ esphome/components/bl0942/sensor.py | 93 ++++++++++++++++++++ tests/test3.yaml | 12 +++ 6 files changed, 296 insertions(+) create mode 100644 esphome/components/bl0942/__init__.py create mode 100644 esphome/components/bl0942/bl0942.cpp create mode 100644 esphome/components/bl0942/bl0942.h create mode 100644 esphome/components/bl0942/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index 0441a6bfcd..e63528fabc 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -35,6 +35,7 @@ esphome/components/bh1750/* @OttoWinter esphome/components/binary_sensor/* @esphome/core esphome/components/bl0939/* @ziceva esphome/components/bl0940/* @tobias- +esphome/components/bl0942/* @dbuezas esphome/components/ble_client/* @buxtronix esphome/components/bluetooth_proxy/* @jesserockz esphome/components/bme680_bsec/* @trvrnrth diff --git a/esphome/components/bl0942/__init__.py b/esphome/components/bl0942/__init__.py new file mode 100644 index 0000000000..8ef7857b7b --- /dev/null +++ b/esphome/components/bl0942/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@dbuezas"] diff --git a/esphome/components/bl0942/bl0942.cpp b/esphome/components/bl0942/bl0942.cpp new file mode 100644 index 0000000000..e6d18a82a7 --- /dev/null +++ b/esphome/components/bl0942/bl0942.cpp @@ -0,0 +1,121 @@ +#include "bl0942.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace bl0942 { + +static const char *const TAG = "bl0942"; + +static const uint8_t BL0942_READ_COMMAND = 0x58; +static const uint8_t BL0942_FULL_PACKET = 0xAA; +static const uint8_t BL0942_PACKET_HEADER = 0x55; + +static const uint8_t BL0942_WRITE_COMMAND = 0xA8; +static const uint8_t BL0942_REG_I_FAST_RMS_CTRL = 0x10; +static const uint8_t BL0942_REG_MODE = 0x18; +static const uint8_t BL0942_REG_SOFT_RESET = 0x19; +static const uint8_t BL0942_REG_USR_WRPROT = 0x1A; +static const uint8_t BL0942_REG_TPS_CTRL = 0x1B; + +// TODO: Confirm insialisation works as intended +const uint8_t BL0942_INIT[5][6] = { + // Reset to default + {BL0942_WRITE_COMMAND, BL0942_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x38}, + // Enable User Operation Write + {BL0942_WRITE_COMMAND, BL0942_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xF0}, + // 0x0100 = CF_UNABLE energy pulse, AC_FREQ_SEL 50Hz, RMS_UPDATE_SEL 800mS + {BL0942_WRITE_COMMAND, BL0942_REG_MODE, 0x00, 0x10, 0x00, 0x37}, + // 0x47FF = Over-current and leakage alarm on, Automatic temperature measurement, Interval 100mS + {BL0942_WRITE_COMMAND, BL0942_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xFE}, + // 0x181C = Half cycle, Fast RMS threshold 6172 + {BL0942_WRITE_COMMAND, BL0942_REG_I_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x1B}}; + +void BL0942::loop() { + DataPacket buffer; + if (!this->available()) { + return; + } + if (read_array((uint8_t *) &buffer, sizeof(buffer))) { + if (validate_checksum(&buffer)) { + received_package_(&buffer); + } + } else { + ESP_LOGW(TAG, "Junk on wire. Throwing away partial message"); + while (read() >= 0) + ; + } +} + +bool BL0942::validate_checksum(DataPacket *data) { + uint8_t checksum = BL0942_READ_COMMAND; + // Whole package but checksum + uint8_t *raw = (uint8_t *) data; + for (uint32_t i = 0; i < sizeof(*data) - 1; i++) { + checksum += raw[i]; + } + checksum ^= 0xFF; + if (checksum != data->checksum) { + ESP_LOGW(TAG, "BL0942 invalid checksum! 0x%02X != 0x%02X", checksum, data->checksum); + } + return checksum == data->checksum; +} + +void BL0942::update() { + this->flush(); + this->write_byte(BL0942_READ_COMMAND); + this->write_byte(BL0942_FULL_PACKET); +} + +void BL0942::setup() { + for (auto *i : BL0942_INIT) { + this->write_array(i, 6); + delay(1); + } + this->flush(); +} + +void BL0942::received_package_(DataPacket *data) { + // Bad header + if (data->frame_header != BL0942_PACKET_HEADER) { + ESP_LOGI(TAG, "Invalid data. Header mismatch: %d", data->frame_header); + return; + } + + float v_rms = (uint24_t) data->v_rms / voltage_reference_; + float i_rms = (uint24_t) data->i_rms / current_reference_; + float watt = (int24_t) data->watt / power_reference_; + uint32_t cf_cnt = (uint24_t) data->cf_cnt; + float total_energy_consumption = cf_cnt / energy_reference_; + float frequency = 1000000.0f / data->frequency; + + if (voltage_sensor_ != nullptr) { + voltage_sensor_->publish_state(v_rms); + } + if (current_sensor_ != nullptr) { + current_sensor_->publish_state(i_rms); + } + if (power_sensor_ != nullptr) { + power_sensor_->publish_state(watt); + } + if (energy_sensor_ != nullptr) { + energy_sensor_->publish_state(total_energy_consumption); + } + if (frequency_sensor_ != nullptr) { + frequency_sensor_->publish_state(frequency); + } + + ESP_LOGV(TAG, "BL0942: U %fV, I %fA, P %fW, Cnt %d, ∫P %fkWh, frequency %f°Hz, status 0x%08X", v_rms, i_rms, watt, + cf_cnt, total_energy_consumption, frequency, data->status); +} + +void BL0942::dump_config() { // NOLINT(readability-function-cognitive-complexity) + ESP_LOGCONFIG(TAG, "BL0942:"); + LOG_SENSOR("", "Voltage", this->voltage_sensor_); + LOG_SENSOR("", "Current", this->current_sensor_); + LOG_SENSOR("", "Power", this->power_sensor_); + LOG_SENSOR("", "Energy", this->energy_sensor_); + LOG_SENSOR("", "frequency", this->frequency_sensor_); +} + +} // namespace bl0942 +} // namespace esphome diff --git a/esphome/components/bl0942/bl0942.h b/esphome/components/bl0942/bl0942.h new file mode 100644 index 0000000000..8149b7493b --- /dev/null +++ b/esphome/components/bl0942/bl0942.h @@ -0,0 +1,68 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/datatypes.h" +#include "esphome/components/uart/uart.h" +#include "esphome/components/sensor/sensor.h" + +namespace esphome { +namespace bl0942 { + +static const float BL0942_PREF = 596; // taken from tasmota +static const float BL0942_UREF = 15873.35944299; // should be 73989/1.218 +static const float BL0942_IREF = 251213.46469622; // 305978/1.218 +static const float BL0942_EREF = 3304.61127328; // Measured + +struct DataPacket { + uint8_t frame_header; + uint24_le_t i_rms; + uint24_le_t v_rms; + uint24_le_t i_fast_rms; + int24_le_t watt; + uint24_le_t cf_cnt; + uint16_le_t frequency; + uint8_t reserved1; + uint8_t status; + uint8_t reserved2; + uint8_t reserved3; + uint8_t checksum; +} __attribute__((packed)); + +class BL0942 : public PollingComponent, public uart::UARTDevice { + public: + void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; } + void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; } + void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; } + void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; } + void set_frequency_sensor(sensor::Sensor *frequency_sensor) { frequency_sensor_ = frequency_sensor; } + + void loop() override; + + void update() override; + void setup() override; + void dump_config() override; + + protected: + sensor::Sensor *voltage_sensor_; + sensor::Sensor *current_sensor_; + // NB This may be negative as the circuits is seemingly able to measure + // power in both directions + sensor::Sensor *power_sensor_; + sensor::Sensor *energy_sensor_; + sensor::Sensor *frequency_sensor_; + + // Divide by this to turn into Watt + float power_reference_ = BL0942_PREF; + // Divide by this to turn into Volt + float voltage_reference_ = BL0942_UREF; + // Divide by this to turn into Ampere + float current_reference_ = BL0942_IREF; + // Divide by this to turn into kWh + float energy_reference_ = BL0942_EREF; + + static bool validate_checksum(DataPacket *data); + + void received_package_(DataPacket *data); +}; +} // namespace bl0942 +} // namespace esphome diff --git a/esphome/components/bl0942/sensor.py b/esphome/components/bl0942/sensor.py new file mode 100644 index 0000000000..f23375b309 --- /dev/null +++ b/esphome/components/bl0942/sensor.py @@ -0,0 +1,93 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor, uart +from esphome.const import ( + CONF_CURRENT, + CONF_ENERGY, + CONF_ID, + CONF_POWER, + CONF_VOLTAGE, + CONF_FREQUENCY, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_ENERGY, + DEVICE_CLASS_POWER, + DEVICE_CLASS_VOLTAGE, + DEVICE_CLASS_FREQUENCY, + STATE_CLASS_MEASUREMENT, + UNIT_AMPERE, + UNIT_KILOWATT_HOURS, + UNIT_VOLT, + UNIT_WATT, + UNIT_HERTZ, +) + +DEPENDENCIES = ["uart"] + +bl0942_ns = cg.esphome_ns.namespace("bl0942") +BL0942 = bl0942_ns.class_("BL0942", cg.PollingComponent, uart.UARTDevice) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(BL0942), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( + unit_of_measurement=UNIT_VOLT, + accuracy_decimals=1, + device_class=DEVICE_CLASS_VOLTAGE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_CURRENT): sensor.sensor_schema( + unit_of_measurement=UNIT_AMPERE, + accuracy_decimals=2, + device_class=DEVICE_CLASS_CURRENT, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_POWER): sensor.sensor_schema( + unit_of_measurement=UNIT_WATT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_POWER, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_ENERGY): sensor.sensor_schema( + unit_of_measurement=UNIT_KILOWATT_HOURS, + accuracy_decimals=0, + device_class=DEVICE_CLASS_ENERGY, + ), + cv.Optional(CONF_FREQUENCY): sensor.sensor_schema( + unit_of_measurement=UNIT_HERTZ, + accuracy_decimals=0, + device_class=DEVICE_CLASS_FREQUENCY, + state_class=STATE_CLASS_MEASUREMENT, + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(uart.UART_DEVICE_SCHEMA) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await uart.register_uart_device(var, config) + + if CONF_VOLTAGE in config: + conf = config[CONF_VOLTAGE] + sens = await sensor.new_sensor(conf) + cg.add(var.set_voltage_sensor(sens)) + if CONF_CURRENT in config: + conf = config[CONF_CURRENT] + sens = await sensor.new_sensor(conf) + cg.add(var.set_current_sensor(sens)) + if CONF_POWER in config: + conf = config[CONF_POWER] + sens = await sensor.new_sensor(conf) + cg.add(var.set_power_sensor(sens)) + if CONF_ENERGY in config: + conf = config[CONF_ENERGY] + sens = await sensor.new_sensor(conf) + cg.add(var.set_energy_sensor(sens)) + if CONF_FREQUENCY in config: + conf = config[CONF_FREQUENCY] + sens = await sensor.new_sensor(conf) + cg.add(var.set_frequency_sensor(sens)) diff --git a/tests/test3.yaml b/tests/test3.yaml index de0fc0dfdb..1d4b4fb076 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -530,6 +530,18 @@ sensor: name: BL0940 Internal temperature external_temperature: name: BL0940 External temperature + - platform: bl0942 + uart_id: uart3 + voltage: + name: 'BL0942 Voltage' + current: + name: 'BL0942 Current' + power: + name: 'BL0942 Power' + energy: + name: 'BL0942 Energy' + frequency: + name: "BL0942 Frequency" - platform: pzem004t uart_id: uart3 voltage: From 790280ace93b153a6c255ca94edcb99dafa2436d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Sep 2022 10:16:18 +1200 Subject: [PATCH 54/66] Bump pylint from 2.15.0 to 2.15.2 (#3785) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 1d57fef954..5ef791906d 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,4 +1,4 @@ -pylint==2.15.0 +pylint==2.15.2 flake8==5.0.4 black==22.8.0 # also change in .pre-commit-config.yaml when updating pyupgrade==2.37.3 # also change in .pre-commit-config.yaml when updating From 9a5f865eeabc3aab37c70debe8c19d33cac8045a Mon Sep 17 00:00:00 2001 From: anatoly-savchenkov <48646998+anatoly-savchenkov@users.noreply.github.com> Date: Mon, 12 Sep 2022 01:23:46 +0300 Subject: [PATCH 55/66] Add Factory Reset button and switch (#3724) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/esp32/preferences.cpp | 13 ++++++- esphome/components/esp8266/preferences.cpp | 21 +++++++++-- esphome/components/factory_reset/__init__.py | 5 +++ .../factory_reset/button/__init__.py | 30 ++++++++++++++++ .../button/factory_reset_button.cpp | 21 +++++++++++ .../button/factory_reset_button.h | 18 ++++++++++ .../factory_reset/switch/__init__.py | 35 +++++++++++++++++++ .../switch/factory_reset_switch.cpp | 26 ++++++++++++++ .../switch/factory_reset_switch.h | 18 ++++++++++ esphome/core/preferences.h | 8 +++++ tests/test1.yaml | 2 ++ tests/test3.yaml | 3 ++ 13 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 esphome/components/factory_reset/__init__.py create mode 100644 esphome/components/factory_reset/button/__init__.py create mode 100644 esphome/components/factory_reset/button/factory_reset_button.cpp create mode 100644 esphome/components/factory_reset/button/factory_reset_button.h create mode 100644 esphome/components/factory_reset/switch/__init__.py create mode 100644 esphome/components/factory_reset/switch/factory_reset_switch.cpp create mode 100644 esphome/components/factory_reset/switch/factory_reset_switch.h diff --git a/CODEOWNERS b/CODEOWNERS index e63528fabc..85ac107975 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -77,6 +77,7 @@ esphome/components/esp32_improv/* @jesserockz esphome/components/esp8266/* @esphome/core esphome/components/exposure_notifications/* @OttoWinter esphome/components/ezo/* @ssieb +esphome/components/factory_reset/* @anatoly-savchenkov esphome/components/fastled_base/* @OttoWinter esphome/components/feedback/* @ianchi esphome/components/fingerprint_grow/* @OnFreund @loongyh diff --git a/esphome/components/esp32/preferences.cpp b/esphome/components/esp32/preferences.cpp index d070a20d82..6a6305cf87 100644 --- a/esphome/components/esp32/preferences.cpp +++ b/esphome/components/esp32/preferences.cpp @@ -141,7 +141,7 @@ class ESP32Preferences : public ESPPreferences { ESP_LOGD(TAG, "Saving %d preferences to flash: %d cached, %d written, %d failed", cached + written + failed, cached, written, failed); if (failed > 0) { - ESP_LOGD(TAG, "Error saving %d preferences to flash. Last error=%s for key=%s", failed, esp_err_to_name(last_err), + ESP_LOGE(TAG, "Error saving %d preferences to flash. Last error=%s for key=%s", failed, esp_err_to_name(last_err), last_key.c_str()); } @@ -170,6 +170,17 @@ class ESP32Preferences : public ESPPreferences { } return to_save.data != stored_data.data; } + + bool reset() override { + ESP_LOGD(TAG, "Cleaning up preferences in flash..."); + s_pending_save.clear(); + + nvs_flash_deinit(); + nvs_flash_erase(); + // Make the handle invalid to prevent any saves until restart + nvs_handle = 0; + return true; + } }; void setup_preferences() { diff --git a/esphome/components/esp8266/preferences.cpp b/esphome/components/esp8266/preferences.cpp index 0e42cea576..1bd20f16ae 100644 --- a/esphome/components/esp8266/preferences.cpp +++ b/esphome/components/esp8266/preferences.cpp @@ -243,17 +243,34 @@ class ESP8266Preferences : public ESPPreferences { } } if (erase_res != SPI_FLASH_RESULT_OK) { - ESP_LOGV(TAG, "Erase ESP8266 flash failed!"); + ESP_LOGE(TAG, "Erase ESP8266 flash failed!"); return false; } if (write_res != SPI_FLASH_RESULT_OK) { - ESP_LOGV(TAG, "Write ESP8266 flash failed!"); + ESP_LOGE(TAG, "Write ESP8266 flash failed!"); return false; } s_flash_dirty = false; return true; } + + bool reset() override { + ESP_LOGD(TAG, "Cleaning up preferences in flash..."); + SpiFlashOpResult erase_res; + { + InterruptLock lock; + erase_res = spi_flash_erase_sector(get_esp8266_flash_sector()); + } + if (erase_res != SPI_FLASH_RESULT_OK) { + ESP_LOGE(TAG, "Erase ESP8266 flash failed!"); + return false; + } + + // Protect flash from writing till restart + s_prevent_write = true; + return true; + } }; void setup_preferences() { diff --git a/esphome/components/factory_reset/__init__.py b/esphome/components/factory_reset/__init__.py new file mode 100644 index 0000000000..f1bcfd8c55 --- /dev/null +++ b/esphome/components/factory_reset/__init__.py @@ -0,0 +1,5 @@ +import esphome.codegen as cg + +CODEOWNERS = ["@anatoly-savchenkov"] + +factory_reset_ns = cg.esphome_ns.namespace("factory_reset") diff --git a/esphome/components/factory_reset/button/__init__.py b/esphome/components/factory_reset/button/__init__.py new file mode 100644 index 0000000000..d5beac34b5 --- /dev/null +++ b/esphome/components/factory_reset/button/__init__.py @@ -0,0 +1,30 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import button +from esphome.const import ( + CONF_ID, + DEVICE_CLASS_RESTART, + ENTITY_CATEGORY_CONFIG, + ICON_RESTART_ALERT, +) +from .. import factory_reset_ns + +FactoryResetButton = factory_reset_ns.class_( + "FactoryResetButton", button.Button, cg.Component +) + +CONFIG_SCHEMA = ( + button.button_schema( + device_class=DEVICE_CLASS_RESTART, + entity_category=ENTITY_CATEGORY_CONFIG, + icon=ICON_RESTART_ALERT, + ) + .extend({cv.GenerateID(): cv.declare_id(FactoryResetButton)}) + .extend(cv.COMPONENT_SCHEMA) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await button.register_button(var, config) diff --git a/esphome/components/factory_reset/button/factory_reset_button.cpp b/esphome/components/factory_reset/button/factory_reset_button.cpp new file mode 100644 index 0000000000..9354a3363e --- /dev/null +++ b/esphome/components/factory_reset/button/factory_reset_button.cpp @@ -0,0 +1,21 @@ +#include "factory_reset_button.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include "esphome/core/application.h" + +namespace esphome { +namespace factory_reset { + +static const char *const TAG = "factory_reset.button"; + +void FactoryResetButton::dump_config() { LOG_BUTTON("", "Factory Reset Button", this); } +void FactoryResetButton::press_action() { + ESP_LOGI(TAG, "Resetting to factory defaults..."); + // Let MQTT settle a bit + delay(100); // NOLINT + global_preferences->reset(); + App.safe_reboot(); +} + +} // namespace factory_reset +} // namespace esphome diff --git a/esphome/components/factory_reset/button/factory_reset_button.h b/esphome/components/factory_reset/button/factory_reset_button.h new file mode 100644 index 0000000000..9996a860d9 --- /dev/null +++ b/esphome/components/factory_reset/button/factory_reset_button.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/button/button.h" + +namespace esphome { +namespace factory_reset { + +class FactoryResetButton : public button::Button, public Component { + public: + void dump_config() override; + + protected: + void press_action() override; +}; + +} // namespace factory_reset +} // namespace esphome diff --git a/esphome/components/factory_reset/switch/__init__.py b/esphome/components/factory_reset/switch/__init__.py new file mode 100644 index 0000000000..3cc19a35a3 --- /dev/null +++ b/esphome/components/factory_reset/switch/__init__.py @@ -0,0 +1,35 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import switch +from esphome.const import ( + CONF_ENTITY_CATEGORY, + CONF_ID, + CONF_INVERTED, + CONF_ICON, + ENTITY_CATEGORY_CONFIG, + ICON_RESTART_ALERT, +) +from .. import factory_reset_ns + +FactoryResetSwitch = factory_reset_ns.class_( + "FactoryResetSwitch", switch.Switch, cg.Component +) + +CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(FactoryResetSwitch), + cv.Optional(CONF_INVERTED): cv.invalid( + "Factory Reset switches do not support inverted mode!" + ), + cv.Optional(CONF_ICON, default=ICON_RESTART_ALERT): cv.icon, + cv.Optional( + CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_CONFIG + ): cv.entity_category, + } +).extend(cv.COMPONENT_SCHEMA) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await switch.register_switch(var, config) diff --git a/esphome/components/factory_reset/switch/factory_reset_switch.cpp b/esphome/components/factory_reset/switch/factory_reset_switch.cpp new file mode 100644 index 0000000000..7bc8676736 --- /dev/null +++ b/esphome/components/factory_reset/switch/factory_reset_switch.cpp @@ -0,0 +1,26 @@ +#include "factory_reset_switch.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include "esphome/core/application.h" + +namespace esphome { +namespace factory_reset { + +static const char *const TAG = "factory_reset.switch"; + +void FactoryResetSwitch::dump_config() { LOG_SWITCH("", "Factory Reset Switch", this); } +void FactoryResetSwitch::write_state(bool state) { + // Acknowledge + this->publish_state(false); + + if (state) { + ESP_LOGI(TAG, "Resetting to factory defaults..."); + // Let MQTT settle a bit + delay(100); // NOLINT + global_preferences->reset(); + App.safe_reboot(); + } +} + +} // namespace factory_reset +} // namespace esphome diff --git a/esphome/components/factory_reset/switch/factory_reset_switch.h b/esphome/components/factory_reset/switch/factory_reset_switch.h new file mode 100644 index 0000000000..2c914ea76d --- /dev/null +++ b/esphome/components/factory_reset/switch/factory_reset_switch.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/switch/switch.h" + +namespace esphome { +namespace factory_reset { + +class FactoryResetSwitch : public switch_::Switch, public Component { + public: + void dump_config() override; + + protected: + void write_state(bool state) override; +}; + +} // namespace factory_reset +} // namespace esphome diff --git a/esphome/core/preferences.h b/esphome/core/preferences.h index 2b13061a59..6d2dd967e9 100644 --- a/esphome/core/preferences.h +++ b/esphome/core/preferences.h @@ -46,6 +46,14 @@ class ESPPreferences { */ virtual bool sync() = 0; + /** + * Forget all unsaved changes and re-initialize the permanent preferences storage. + * Usually followed by a restart which moves the system to "factory" conditions + * + * @return true if operation is successful. + */ + virtual bool reset() = 0; + template::value, bool> = true> ESPPreferenceObject make_preference(uint32_t type, bool in_flash) { return this->make_preference(sizeof(T), type, in_flash); diff --git a/tests/test1.yaml b/tests/test1.yaml index e5e9754d74..e213a8b041 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2178,6 +2178,8 @@ switch: name: Living Room Restart - platform: safe_mode name: Living Room Restart (Safe Mode) + - platform: factory_reset + name: Living Room Restart (Factory Default Settings) - platform: shutdown name: Living Room Shutdown - platform: output diff --git a/tests/test3.yaml b/tests/test3.yaml index 1d4b4fb076..4eee0fd2c9 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -1529,6 +1529,9 @@ button: target_mac_address: 12:34:56:78:90:ab name: wol_test_1 id: wol_1 + - platform: factory_reset + name: Restart Button (Factory Default Settings) + cd74hc4067: pin_s0: GPIO12 From be473b97c49562996fa0a257a6e31b933eb0b431 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Saura Date: Mon, 12 Sep 2022 00:32:07 +0200 Subject: [PATCH 56/66] [MPU6050] Support devices with WHOAMI 0x98 (#3784) --- esphome/components/mpu6050/mpu6050.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/mpu6050/mpu6050.cpp b/esphome/components/mpu6050/mpu6050.cpp index 8a0756e63c..cc426e58a2 100644 --- a/esphome/components/mpu6050/mpu6050.cpp +++ b/esphome/components/mpu6050/mpu6050.cpp @@ -23,7 +23,7 @@ const float GRAVITY_EARTH = 9.80665f; void MPU6050Component::setup() { ESP_LOGCONFIG(TAG, "Setting up MPU6050..."); uint8_t who_am_i; - if (!this->read_byte(MPU6050_REGISTER_WHO_AM_I, &who_am_i) || who_am_i != 0x68) { + if (!this->read_byte(MPU6050_REGISTER_WHO_AM_I, &who_am_i) || (who_am_i != 0x68 && who_am_i != 0x98)) { this->mark_failed(); return; } From 9ff187c3f8b2e2653ca1c49e274cbcc735b224c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Sep 2022 10:32:33 +1200 Subject: [PATCH 57/66] Bump zeroconf from 0.39.0 to 0.39.1 (#3782) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f4cb4285ab..bd0c70bb72 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ esptool==3.3.1 click==8.1.3 esphome-dashboard==20220508.0 aioesphomeapi==10.13.0 -zeroconf==0.39.0 +zeroconf==0.39.1 # esp-idf requires this, but doesn't bundle it by default # https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 From cbd8d70431decfcaaf1c36e931a693f9ca398d30 Mon Sep 17 00:00:00 2001 From: "Jordan W. Cobb" Date: Mon, 12 Sep 2022 11:30:15 -0400 Subject: [PATCH 58/66] Add support for TM1638 Led and Key component (#3340) --- CODEOWNERS | 1 + esphome/components/tm1638/__init__.py | 0 .../tm1638/binary_sensor/__init__.py | 22 ++ .../tm1638/binary_sensor/tm1638_key.cpp | 13 + .../tm1638/binary_sensor/tm1638_key.h | 19 ++ esphome/components/tm1638/display.py | 55 ++++ esphome/components/tm1638/output/__init__.py | 25 ++ .../tm1638/output/tm1638_output_led.cpp | 17 ++ .../tm1638/output/tm1638_output_led.h | 25 ++ esphome/components/tm1638/sevenseg.h | 107 +++++++ esphome/components/tm1638/switch/__init__.py | 24 ++ .../tm1638/switch/tm1638_switch_led.cpp | 20 ++ .../tm1638/switch/tm1638_switch_led.h | 23 ++ esphome/components/tm1638/tm1638.cpp | 288 ++++++++++++++++++ esphome/components/tm1638/tm1638.h | 81 +++++ esphome/const.py | 2 + tests/test5.yaml | 133 ++++++++ 17 files changed, 855 insertions(+) create mode 100644 esphome/components/tm1638/__init__.py create mode 100644 esphome/components/tm1638/binary_sensor/__init__.py create mode 100644 esphome/components/tm1638/binary_sensor/tm1638_key.cpp create mode 100644 esphome/components/tm1638/binary_sensor/tm1638_key.h create mode 100644 esphome/components/tm1638/display.py create mode 100644 esphome/components/tm1638/output/__init__.py create mode 100644 esphome/components/tm1638/output/tm1638_output_led.cpp create mode 100644 esphome/components/tm1638/output/tm1638_output_led.h create mode 100644 esphome/components/tm1638/sevenseg.h create mode 100644 esphome/components/tm1638/switch/__init__.py create mode 100644 esphome/components/tm1638/switch/tm1638_switch_led.cpp create mode 100644 esphome/components/tm1638/switch/tm1638_switch_led.h create mode 100644 esphome/components/tm1638/tm1638.cpp create mode 100644 esphome/components/tm1638/tm1638.h diff --git a/CODEOWNERS b/CODEOWNERS index 85ac107975..3a1087191c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -230,6 +230,7 @@ esphome/components/time/* @OttoWinter esphome/components/tlc5947/* @rnauber esphome/components/tm1621/* @Philippe12 esphome/components/tm1637/* @glmnet +esphome/components/tm1638/* @skykingjwc esphome/components/tmp102/* @timsavage esphome/components/tmp117/* @Azimath esphome/components/tof10120/* @wstrzalka diff --git a/esphome/components/tm1638/__init__.py b/esphome/components/tm1638/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/tm1638/binary_sensor/__init__.py b/esphome/components/tm1638/binary_sensor/__init__.py new file mode 100644 index 0000000000..7262d9e9e1 --- /dev/null +++ b/esphome/components/tm1638/binary_sensor/__init__.py @@ -0,0 +1,22 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import binary_sensor +from esphome.const import CONF_KEY +from ..display import tm1638_ns, TM1638Component, CONF_TM1638_ID + +TM1638Key = tm1638_ns.class_("TM1638Key", binary_sensor.BinarySensor) + +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TM1638Key), + cv.GenerateID(CONF_TM1638_ID): cv.use_id(TM1638Component), + cv.Required(CONF_KEY): cv.int_range(min=0, max=15), + } +) + + +async def to_code(config): + var = await binary_sensor.new_binary_sensor(config) + cg.add(var.set_keycode(config[CONF_KEY])) + hub = await cg.get_variable(config[CONF_TM1638_ID]) + cg.add(hub.register_listener(var)) diff --git a/esphome/components/tm1638/binary_sensor/tm1638_key.cpp b/esphome/components/tm1638/binary_sensor/tm1638_key.cpp new file mode 100644 index 0000000000..c143bafaea --- /dev/null +++ b/esphome/components/tm1638/binary_sensor/tm1638_key.cpp @@ -0,0 +1,13 @@ +#include "tm1638_key.h" + +namespace esphome { +namespace tm1638 { + +void TM1638Key::keys_update(uint8_t keys) { + bool pressed = keys & (1 << key_code_); + if (pressed != this->state) + this->publish_state(pressed); +} + +} // namespace tm1638 +} // namespace esphome diff --git a/esphome/components/tm1638/binary_sensor/tm1638_key.h b/esphome/components/tm1638/binary_sensor/tm1638_key.h new file mode 100644 index 0000000000..0ea385f434 --- /dev/null +++ b/esphome/components/tm1638/binary_sensor/tm1638_key.h @@ -0,0 +1,19 @@ +#pragma once + +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "../tm1638.h" + +namespace esphome { +namespace tm1638 { + +class TM1638Key : public binary_sensor::BinarySensor, public KeyListener { + public: + void set_keycode(uint8_t key_code) { key_code_ = key_code; }; + void keys_update(uint8_t keys) override; + + protected: + uint8_t key_code_{0}; +}; + +} // namespace tm1638 +} // namespace esphome diff --git a/esphome/components/tm1638/display.py b/esphome/components/tm1638/display.py new file mode 100644 index 0000000000..6339983674 --- /dev/null +++ b/esphome/components/tm1638/display.py @@ -0,0 +1,55 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.components import display +from esphome.const import ( + CONF_ID, + CONF_INTENSITY, + CONF_LAMBDA, + CONF_CLK_PIN, + CONF_DIO_PIN, + CONF_STB_PIN, +) + +CODEOWNERS = ["@skykingjwc"] + +CONF_TM1638_ID = "tm1638_id" + +tm1638_ns = cg.esphome_ns.namespace("tm1638") +TM1638Component = tm1638_ns.class_("TM1638Component", cg.PollingComponent) +TM1638ComponentRef = TM1638Component.operator("ref") + + +CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TM1638Component), + cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_STB_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_DIO_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_INTENSITY, default=7): cv.int_range(min=0, max=8), + } +).extend(cv.polling_component_schema("1s")) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await display.register_display(var, config) + + clk = await cg.gpio_pin_expression(config[CONF_CLK_PIN]) + cg.add(var.set_clk_pin(clk)) + + dio = await cg.gpio_pin_expression(config[CONF_DIO_PIN]) + cg.add(var.set_dio_pin(dio)) + + stb = await cg.gpio_pin_expression(config[CONF_STB_PIN]) + cg.add(var.set_stb_pin(stb)) + + cg.add(var.set_intensity(config[CONF_INTENSITY])) + + if CONF_LAMBDA in config: + lambda_ = await cg.process_lambda( + config[CONF_LAMBDA], [(TM1638ComponentRef, "it")], return_type=cg.void + ) + + cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/tm1638/output/__init__.py b/esphome/components/tm1638/output/__init__.py new file mode 100644 index 0000000000..2d982e409d --- /dev/null +++ b/esphome/components/tm1638/output/__init__.py @@ -0,0 +1,25 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import output +from esphome.const import CONF_ID, CONF_LED +from ..display import tm1638_ns, TM1638Component, CONF_TM1638_ID + +TM1638OutputLed = tm1638_ns.class_("TM1638OutputLed", output.BinaryOutput, cg.Component) + + +CONFIG_SCHEMA = output.BINARY_OUTPUT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TM1638OutputLed), + cv.GenerateID(CONF_TM1638_ID): cv.use_id(TM1638Component), + cv.Required(CONF_LED): cv.int_range(min=0, max=7), + } +).extend(cv.COMPONENT_SCHEMA) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await output.register_output(var, config) + await cg.register_component(var, config) + cg.add(var.set_lednum(config[CONF_LED])) + hub = await cg.get_variable(config[CONF_TM1638_ID]) + cg.add(var.set_tm1638(hub)) diff --git a/esphome/components/tm1638/output/tm1638_output_led.cpp b/esphome/components/tm1638/output/tm1638_output_led.cpp new file mode 100644 index 0000000000..ea1c84e64b --- /dev/null +++ b/esphome/components/tm1638/output/tm1638_output_led.cpp @@ -0,0 +1,17 @@ +#include "tm1638_output_led.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace tm1638 { + +static const char *const TAG = "tm1638.led"; + +void TM1638OutputLed::write_state(bool state) { tm1638_->set_led(led_, state); } + +void TM1638OutputLed::dump_config() { + LOG_BINARY_OUTPUT(this); + ESP_LOGCONFIG(TAG, " LED: %d", led_); +} + +} // namespace tm1638 +} // namespace esphome diff --git a/esphome/components/tm1638/output/tm1638_output_led.h b/esphome/components/tm1638/output/tm1638_output_led.h new file mode 100644 index 0000000000..6aa1015aae --- /dev/null +++ b/esphome/components/tm1638/output/tm1638_output_led.h @@ -0,0 +1,25 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/output/binary_output.h" +#include "../tm1638.h" + +namespace esphome { +namespace tm1638 { + +class TM1638OutputLed : public output::BinaryOutput, public Component { + public: + void dump_config() override; + + void set_tm1638(TM1638Component *tm1638) { tm1638_ = tm1638; } + void set_lednum(int led) { led_ = led; } + + protected: + void write_state(bool state) override; + + TM1638Component *tm1638_; + int led_; +}; + +} // namespace tm1638 +} // namespace esphome diff --git a/esphome/components/tm1638/sevenseg.h b/esphome/components/tm1638/sevenseg.h new file mode 100644 index 0000000000..e20a55a69f --- /dev/null +++ b/esphome/components/tm1638/sevenseg.h @@ -0,0 +1,107 @@ +#pragma once + +namespace esphome { +namespace tm1638 { +namespace TM1638Translation { + +const unsigned char SEVEN_SEG[] PROGMEM = { + 0x00, /* (space) */ + 0x86, /* ! */ + 0x22, /* " */ + 0x7E, /* # */ + 0x6D, /* $ */ + 0xD2, /* % */ + 0x46, /* & */ + 0x20, /* ' */ + 0x29, /* ( */ + 0x0B, /* ) */ + 0x21, /* * */ + 0x70, /* + */ + 0x10, /* , */ + 0x40, /* - */ + 0x80, /* . */ + 0x52, /* / */ + 0x3F, /* 0 */ + 0x06, /* 1 */ + 0x5B, /* 2 */ + 0x4F, /* 3 */ + 0x66, /* 4 */ + 0x6D, /* 5 */ + 0x7D, /* 6 */ + 0x07, /* 7 */ + 0x7F, /* 8 */ + 0x6F, /* 9 */ + 0x09, /* : */ + 0x0D, /* ; */ + 0x61, /* < */ + 0x48, /* = */ + 0x43, /* > */ + 0xD3, /* ? */ + 0x5F, /* @ */ + 0x77, /* A */ + 0x7C, /* B */ + 0x39, /* C */ + 0x5E, /* D */ + 0x79, /* E */ + 0x71, /* F */ + 0x3D, /* G */ + 0x76, /* H */ + 0x30, /* I */ + 0x1E, /* J */ + 0x75, /* K */ + 0x38, /* L */ + 0x15, /* M */ + 0x37, /* N */ + 0x3F, /* O */ + 0x73, /* P */ + 0x6B, /* Q */ + 0x33, /* R */ + 0x6D, /* S */ + 0x78, /* T */ + 0x3E, /* U */ + 0x3E, /* V */ + 0x2A, /* W */ + 0x76, /* X */ + 0x6E, /* Y */ + 0x5B, /* Z */ + 0x39, /* [ */ + 0x64, /* \ */ + 0x0F, /* ] */ + 0x23, /* ^ */ + 0x08, /* _ */ + 0x02, /* ` */ + 0x5F, /* a */ + 0x7C, /* b */ + 0x58, /* c */ + 0x5E, /* d */ + 0x7B, /* e */ + 0x71, /* f */ + 0x6F, /* g */ + 0x74, /* h */ + 0x10, /* i */ + 0x0C, /* j */ + 0x75, /* k */ + 0x30, /* l */ + 0x14, /* m */ + 0x54, /* n */ + 0x5C, /* o */ + 0x73, /* p */ + 0x67, /* q */ + 0x50, /* r */ + 0x6D, /* s */ + 0x78, /* t */ + 0x1C, /* u */ + 0x1C, /* v */ + 0x14, /* w */ + 0x76, /* x */ + 0x6E, /* y */ + 0x5B, /* z */ + 0x46, /* { */ + 0x30, /* | */ + 0x70, /* } */ + 0x01, /* ~ */ +}; + +}; // namespace TM1638Translation +} // namespace tm1638 +} // namespace esphome diff --git a/esphome/components/tm1638/switch/__init__.py b/esphome/components/tm1638/switch/__init__.py new file mode 100644 index 0000000000..ed6aa91d03 --- /dev/null +++ b/esphome/components/tm1638/switch/__init__.py @@ -0,0 +1,24 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import switch +from esphome.const import CONF_LED +from ..display import tm1638_ns, TM1638Component, CONF_TM1638_ID + +TM1638SwitchLed = tm1638_ns.class_("TM1638SwitchLed", switch.Switch, cg.Component) + + +CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TM1638SwitchLed), + cv.GenerateID(CONF_TM1638_ID): cv.use_id(TM1638Component), + cv.Required(CONF_LED): cv.int_range(min=0, max=7), + } +).extend(cv.COMPONENT_SCHEMA) + + +async def to_code(config): + var = await switch.new_switch(config) + await cg.register_component(var, config) + cg.add(var.set_lednum(config[CONF_LED])) + hub = await cg.get_variable(config[CONF_TM1638_ID]) + cg.add(var.set_tm1638(hub)) diff --git a/esphome/components/tm1638/switch/tm1638_switch_led.cpp b/esphome/components/tm1638/switch/tm1638_switch_led.cpp new file mode 100644 index 0000000000..60c9e8b4a9 --- /dev/null +++ b/esphome/components/tm1638/switch/tm1638_switch_led.cpp @@ -0,0 +1,20 @@ +#include "tm1638_switch_led.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace tm1638 { + +static const char *const TAG = "tm1638.led"; + +void TM1638SwitchLed::write_state(bool state) { + tm1638_->set_led(led_, state); + publish_state(state); +} + +void TM1638SwitchLed::dump_config() { + LOG_SWITCH("", "TM1638 LED", this); + ESP_LOGCONFIG(TAG, " LED: %d", led_); +} + +} // namespace tm1638 +} // namespace esphome diff --git a/esphome/components/tm1638/switch/tm1638_switch_led.h b/esphome/components/tm1638/switch/tm1638_switch_led.h new file mode 100644 index 0000000000..10516e0079 --- /dev/null +++ b/esphome/components/tm1638/switch/tm1638_switch_led.h @@ -0,0 +1,23 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/switch/switch.h" +#include "../tm1638.h" + +namespace esphome { +namespace tm1638 { + +class TM1638SwitchLed : public switch_::Switch, public Component { + public: + void dump_config() override; + + void set_tm1638(TM1638Component *tm1638) { tm1638_ = tm1638; } + void set_lednum(int led) { led_ = led; } + + protected: + void write_state(bool state) override; + TM1638Component *tm1638_; + int led_; +}; +} // namespace tm1638 +} // namespace esphome diff --git a/esphome/components/tm1638/tm1638.cpp b/esphome/components/tm1638/tm1638.cpp new file mode 100644 index 0000000000..526b53f601 --- /dev/null +++ b/esphome/components/tm1638/tm1638.cpp @@ -0,0 +1,288 @@ +#include "tm1638.h" +#include "sevenseg.h" +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace tm1638 { + +static const char *const TAG = "display.tm1638"; +static const uint8_t TM1638_REGISTER_FIXEDADDRESS = 0x44; +static const uint8_t TM1638_REGISTER_AUTOADDRESS = 0x40; +static const uint8_t TM1638_REGISTER_READBUTTONS = 0x42; +static const uint8_t TM1638_REGISTER_DISPLAYOFF = 0x80; +static const uint8_t TM1638_REGISTER_DISPLAYON = 0x88; +static const uint8_t TM1638_REGISTER_7SEG_0 = 0xC0; +static const uint8_t TM1638_REGISTER_LED_0 = 0xC1; +static const uint8_t TM1638_UNKNOWN_CHAR = 0b11111111; + +static const uint8_t TM1638_SHIFT_DELAY = 4; // clock pause between commands, default 4ms + +void TM1638Component::setup() { + ESP_LOGD(TAG, "Setting up TM1638..."); + + this->clk_pin_->setup(); // OUTPUT + this->dio_pin_->setup(); // OUTPUT + this->stb_pin_->setup(); // OUTPUT + + this->clk_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->stb_pin_->pin_mode(gpio::FLAG_OUTPUT); + + this->clk_pin_->digital_write(false); + this->dio_pin_->digital_write(false); + this->stb_pin_->digital_write(false); + + this->set_intensity(intensity_); + + this->reset_(); // all LEDs off + + for (uint8_t i = 0; i < 8; i++) // zero fill print buffer + this->buffer_[i] = 0; +} + +void TM1638Component::dump_config() { + ESP_LOGCONFIG(TAG, "TM1638:"); + ESP_LOGCONFIG(TAG, " Intensity: %u", this->intensity_); + LOG_PIN(" CLK Pin: ", this->clk_pin_); + LOG_PIN(" DIO Pin: ", this->dio_pin_); + LOG_PIN(" STB Pin: ", this->stb_pin_); + LOG_UPDATE_INTERVAL(this); +} + +void TM1638Component::loop() { + if (this->listeners_.empty()) + return; + + uint8_t keys = this->get_keys(); + for (auto &listener : this->listeners_) + listener->keys_update(keys); +} + +uint8_t TM1638Component::get_keys() { + uint8_t buttons = 0; + + this->stb_pin_->digital_write(false); + + this->shift_out_(TM1638_REGISTER_READBUTTONS); + + this->dio_pin_->pin_mode(gpio::FLAG_INPUT); + + delayMicroseconds(10); + + for (uint8_t i = 0; i < 4; i++) { // read the 4 button registers + uint8_t v = this->shift_in_(); + buttons |= v << i; // shift bits to correct slots in the byte + } + + this->dio_pin_->pin_mode(gpio::FLAG_OUTPUT); + + this->stb_pin_->digital_write(true); + + return buttons; +} + +void TM1638Component::update() { // this is called at the interval specified in the config.yaml + if (this->writer_.has_value()) { + (*this->writer_)(*this); + } + + this->display(); +} + +float TM1638Component::get_setup_priority() const { return setup_priority::PROCESSOR; } + +void TM1638Component::display() { + for (uint8_t i = 0; i < 8; i++) { + this->set_7seg_(i, buffer_[i]); + } +} + +void TM1638Component::reset_() { + uint8_t num_commands = 16; // 16 addresses, 8 for 7seg and 8 for LEDs + uint8_t commands[num_commands]; + + for (uint8_t i = 0; i < num_commands; i++) { + commands[i] = 0; + } + + this->send_command_sequence_(commands, num_commands, TM1638_REGISTER_7SEG_0); +} + +/////////////// LEDs ///////////////// + +void TM1638Component::set_led(int led_pos, bool led_on_off) { + this->send_command_(TM1638_REGISTER_FIXEDADDRESS); + + uint8_t commands[2]; + + commands[0] = TM1638_REGISTER_LED_0 + (led_pos << 1); + commands[1] = led_on_off; + + this->send_commands_(commands, 2); +} + +void TM1638Component::set_7seg_(int seg_pos, uint8_t seg_bits) { + this->send_command_(TM1638_REGISTER_FIXEDADDRESS); + + uint8_t commands[2] = {}; + + commands[0] = TM1638_REGISTER_7SEG_0 + (seg_pos << 1); + commands[1] = seg_bits; + + this->send_commands_(commands, 2); +} + +void TM1638Component::set_intensity(uint8_t brightness_level) { + this->intensity_ = brightness_level; + + this->send_command_(TM1638_REGISTER_FIXEDADDRESS); + + if (brightness_level > 0) { + this->send_command_((uint8_t)(TM1638_REGISTER_DISPLAYON | intensity_)); + } else { + this->send_command_(TM1638_REGISTER_DISPLAYOFF); + } +} + +/////////////// DISPLAY PRINT ///////////////// + +uint8_t TM1638Component::print(uint8_t start_pos, const char *str) { + uint8_t pos = start_pos; + + bool last_was_dot = false; + + for (; *str != '\0'; str++) { + uint8_t data = TM1638_UNKNOWN_CHAR; + + if (*str >= ' ' && *str <= '~') { + data = progmem_read_byte(&TM1638Translation::SEVEN_SEG[*str - 32]); // subract 32 to account for ASCII offset + } else if (data == TM1638_UNKNOWN_CHAR) { + ESP_LOGW(TAG, "Encountered character '%c' with no TM1638 representation while translating string!", *str); + } + + if (*str == '.') // handle dots + { + if (pos != start_pos && + !last_was_dot) // if we are not at the first position, backup by one unless last char was a dot + { + pos--; + } + this->buffer_[pos] |= 0b10000000; // turn on the dot on the previous position + last_was_dot = true; // set a bit in case the next chracter is also a dot + } else // if not a dot, then just write the character to display + { + if (pos >= 8) { + ESP_LOGI(TAG, "TM1638 String is too long for the display!"); + break; + } + this->buffer_[pos] = data; + last_was_dot = false; // clear dot tracking bit + } + + pos++; + } + return pos - start_pos; +} + +/////////////// PRINT ///////////////// + +uint8_t TM1638Component::print(const char *str) { return this->print(0, str); } + +uint8_t TM1638Component::printf(uint8_t pos, const char *format, ...) { + va_list arg; + va_start(arg, format); + char buffer[64]; + int ret = vsnprintf(buffer, sizeof(buffer), format, arg); + va_end(arg); + if (ret > 0) + return this->print(pos, buffer); + return 0; +} +uint8_t TM1638Component::printf(const char *format, ...) { + va_list arg; + va_start(arg, format); + char buffer[64]; + int ret = vsnprintf(buffer, sizeof(buffer), format, arg); + va_end(arg); + if (ret > 0) + return this->print(buffer); + return 0; +} + +#ifdef USE_TIME +uint8_t TM1638Component::strftime(uint8_t pos, const char *format, time::ESPTime time) { + char buffer[64]; + size_t ret = time.strftime(buffer, sizeof(buffer), format); + if (ret > 0) + return this->print(pos, buffer); + return 0; +} +uint8_t TM1638Component::strftime(const char *format, time::ESPTime time) { return this->strftime(0, format, time); } +#endif + +//////////////// SPI //////////////// + +void TM1638Component::send_command_(uint8_t value) { + this->stb_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->stb_pin_->digital_write(false); + this->shift_out_(value); + this->stb_pin_->digital_write(true); +} + +void TM1638Component::send_commands_(uint8_t const commands[], uint8_t num_commands) { + this->stb_pin_->digital_write(false); + + for (uint8_t i = 0; i < num_commands; i++) { + uint8_t command = commands[i]; + this->shift_out_(command); + } + this->stb_pin_->digital_write(true); +} + +void TM1638Component::send_command_leave_open_(uint8_t value) { + this->stb_pin_->digital_write(false); + this->shift_out_(value); +} + +void TM1638Component::send_command_sequence_(uint8_t commands[], uint8_t num_commands, uint8_t starting_address) { + this->send_command_(TM1638_REGISTER_AUTOADDRESS); + this->send_command_leave_open_(starting_address); + + for (uint8_t i = 0; i < num_commands; i++) { + this->shift_out_(commands[i]); + } + + this->stb_pin_->digital_write(true); +} + +uint8_t TM1638Component::shift_in_() { + uint8_t value = 0; + + for (int i = 0; i < 8; ++i) { + value |= dio_pin_->digital_read() << i; + delayMicroseconds(TM1638_SHIFT_DELAY); + this->clk_pin_->digital_write(true); + delayMicroseconds(TM1638_SHIFT_DELAY); + this->clk_pin_->digital_write(false); + delayMicroseconds(TM1638_SHIFT_DELAY); + } + return value; +} + +void TM1638Component::shift_out_(uint8_t val) { + for (int i = 0; i < 8; i++) { + this->dio_pin_->digital_write((val & (1 << i))); + delayMicroseconds(TM1638_SHIFT_DELAY); + + this->clk_pin_->digital_write(true); + delayMicroseconds(TM1638_SHIFT_DELAY); + + this->clk_pin_->digital_write(false); + delayMicroseconds(TM1638_SHIFT_DELAY); + } +} + +} // namespace tm1638 +} // namespace esphome diff --git a/esphome/components/tm1638/tm1638.h b/esphome/components/tm1638/tm1638.h new file mode 100644 index 0000000000..44160ad227 --- /dev/null +++ b/esphome/components/tm1638/tm1638.h @@ -0,0 +1,81 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/defines.h" +#include "esphome/core/automation.h" +#include "esphome/core/hal.h" + +#ifdef USE_TIME +#include "esphome/components/time/real_time_clock.h" +#endif + +namespace esphome { +namespace tm1638 { + +class KeyListener { + public: + virtual void keys_update(uint8_t keys){}; +}; + +class TM1638Component; + +using tm1638_writer_t = std::function; + +class TM1638Component : public PollingComponent { + public: + void set_writer(tm1638_writer_t &&writer) { this->writer_ = writer; } + void setup() override; + void dump_config() override; + void update() override; + float get_setup_priority() const override; + void set_intensity(uint8_t brightness_level); + void display(); + + void set_clk_pin(GPIOPin *pin) { this->clk_pin_ = pin; } + void set_dio_pin(GPIOPin *pin) { this->dio_pin_ = pin; } + void set_stb_pin(GPIOPin *pin) { this->stb_pin_ = pin; } + + void register_listener(KeyListener *listener) { this->listeners_.push_back(listener); } + + /// Evaluate the printf-format and print the result at the given position. + uint8_t printf(uint8_t pos, const char *format, ...) __attribute__((format(printf, 3, 4))); + /// Evaluate the printf-format and print the result at position 0. + uint8_t printf(const char *format, ...) __attribute__((format(printf, 2, 3))); + + /// Print `str` at the given position. + uint8_t print(uint8_t pos, const char *str); + /// Print `str` at position 0. + uint8_t print(const char *str); + + void loop() override; + uint8_t get_keys(); + +#ifdef USE_TIME + /// Evaluate the strftime-format and print the result at the given position. + uint8_t strftime(uint8_t pos, const char *format, time::ESPTime time) __attribute__((format(strftime, 3, 0))); + /// Evaluate the strftime-format and print the result at position 0. + uint8_t strftime(const char *format, time::ESPTime time) __attribute__((format(strftime, 2, 0))); +#endif + + void set_led(int led_pos, bool led_on_off); + + protected: + void set_7seg_(int seg_pos, uint8_t seg_bits); + void send_command_(uint8_t value); + void send_command_leave_open_(uint8_t value); + void send_commands_(uint8_t const commands[], uint8_t num_commands); + void send_command_sequence_(uint8_t commands[], uint8_t num_commands, uint8_t starting_address); + void shift_out_(uint8_t value); + void reset_(); + uint8_t shift_in_(); + uint8_t intensity_{}; /// brghtness of the display 0 through 7 + GPIOPin *clk_pin_; + GPIOPin *stb_pin_; + GPIOPin *dio_pin_; + uint8_t *buffer_ = new uint8_t[8]; + optional writer_{}; + std::vector listeners_{}; +}; + +} // namespace tm1638 +} // namespace esphome diff --git a/esphome/const.py b/esphome/const.py index 51c07ab666..5153efbbe0 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -334,6 +334,7 @@ CONF_LAMBDA = "lambda" CONF_LAST_CONFIDENCE = "last_confidence" CONF_LAST_FINGER_ID = "last_finger_id" CONF_LATITUDE = "latitude" +CONF_LED = "led" CONF_LEGEND = "legend" CONF_LENGTH = "length" CONF_LEVEL = "level" @@ -653,6 +654,7 @@ CONF_STATE_CLASS = "state_class" CONF_STATE_TOPIC = "state_topic" CONF_STATIC_IP = "static_ip" CONF_STATUS = "status" +CONF_STB_PIN = "stb_pin" CONF_STEP = "step" CONF_STEP_MODE = "step_mode" CONF_STEP_PIN = "step_pin" diff --git a/tests/test5.yaml b/tests/test5.yaml index 3b6862a4d4..7fc20c452f 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -80,6 +80,91 @@ binary_sensor: bitmask: 0x80 # (bit 8) lambda: "return x;" + - platform: tm1638 + id: Button0 + key: 0 + filters: + - delayed_on: 10ms + on_press: + then: + - switch.turn_on: Led0 + on_release: + then: + - switch.turn_off: Led0 + + - platform: tm1638 + id: Button1 + key: 1 + on_press: + then: + - switch.turn_on: Led1 + on_release: + then: + - switch.turn_off: Led1 + + - platform: tm1638 + id: Button2 + key: 2 + on_press: + then: + - switch.turn_on: Led2 + on_release: + then: + - switch.turn_off: Led2 + + - platform: tm1638 + id: Button3 + key: 3 + on_press: + then: + - switch.turn_on: Led3 + on_release: + then: + - switch.turn_off: Led3 + + - platform: tm1638 + id: Button4 + key: 4 + on_press: + then: + - output.turn_on: Led4 + on_release: + then: + - output.turn_off: Led4 + + - platform: tm1638 + id: Button5 + key: 5 + on_press: + then: + - output.turn_on: Led5 + on_release: + then: + - output.turn_off: Led5 + + - platform: tm1638 + id: Button6 + key: 6 + on_press: + then: + - output.turn_on: Led6 + on_release: + then: + - output.turn_off: Led6 + + - platform: tm1638 + id: Button7 + key: 7 + on_press: + then: + - output.turn_on: Led7 + on_release: + then: + - output.turn_off: Led7 + + + + tlc5947: data_pin: GPIO12 clock_pin: GPIO14 @@ -106,6 +191,22 @@ output: address: 0x9001 value_type: U_WORD + - platform: tm1638 + id: Led4 + led: 4 + + - platform: tm1638 + id: Led5 + led: 5 + + - platform: tm1638 + id: Led6 + led: 6 + + - platform: tm1638 + id: Led7 + led: 7 + demo: esp32_ble: @@ -354,3 +455,35 @@ switch: register_type: coil address: 2 bitmask: 1 + + - platform: tm1638 + id: Led0 + led: 0 + name: TM1638Led0 + + - platform: tm1638 + id: Led1 + led: 1 + name: TM1638Led1 + + - platform: tm1638 + id: Led2 + led: 2 + name: TM1638Led2 + + - platform: tm1638 + id: Led3 + led: 3 + name: TM1638Led3 + +display: + - platform: tm1638 + id: primarydisplay + stb_pin: 5 #TM1638 STB + clk_pin: 18 #TM1638 CLK + dio_pin: 23 #TM1638 DIO + update_interval: 5s + intensity: 5 + lambda: |- + it.print("81818181"); + From ed8f343aadef51328a26404bf8d006597cf1ff00 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 13 Sep 2022 01:17:33 +0200 Subject: [PATCH 59/66] Remove status_set_error from ufire_ec (#3792) --- esphome/components/ufire_ec/ufire_ec.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/esphome/components/ufire_ec/ufire_ec.cpp b/esphome/components/ufire_ec/ufire_ec.cpp index d7ed890e21..7af4fadf75 100644 --- a/esphome/components/ufire_ec/ufire_ec.cpp +++ b/esphome/components/ufire_ec/ufire_ec.cpp @@ -12,7 +12,6 @@ void UFireECComponent::setup() { uint8_t version; if (!this->read_byte(REGISTER_VERSION, &version) && version != 0xFF) { this->mark_failed(); - this->status_set_error(); return; } ESP_LOGI(TAG, "Found ufire_ec board version 0x%02X", version); From 15f0e54cbfadf65a065797796db4dcfeef6d94c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Sep 2022 09:57:43 +1200 Subject: [PATCH 60/66] Bump frenck/action-yamllint from 1.2.0 to 1.3.0 (#3798) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7496ccf388..b5b7521b3f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -173,7 +173,7 @@ jobs: - name: Run yamllint if: matrix.id == 'yamllint' - uses: frenck/action-yamllint@v1.2.0 + uses: frenck/action-yamllint@v1.3.0 - name: Suggested changes run: script/ci-suggest-changes From 49465223a48a49434434d9a59206c15fea1b1229 Mon Sep 17 00:00:00 2001 From: Jonathan V <11986470+jonofmac@users.noreply.github.com> Date: Tue, 13 Sep 2022 18:10:12 -0500 Subject: [PATCH 61/66] esp32_ble_tracker continuous and one shot scanning modes (#3649) Co-authored-by: Jonathan Valdez <@jonofmac> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../components/esp32_ble_tracker/__init__.py | 39 +++++++++++++++ .../components/esp32_ble_tracker/automation.h | 8 ++++ .../esp32_ble_tracker/esp32_ble_tracker.cpp | 47 +++++++++++++++++-- .../esp32_ble_tracker/esp32_ble_tracker.h | 21 +++++++++ 4 files changed, 112 insertions(+), 3 deletions(-) diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index e647b74a8f..db7fcc68ae 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -23,6 +23,8 @@ CONF_ESP32_BLE_ID = "esp32_ble_id" CONF_SCAN_PARAMETERS = "scan_parameters" CONF_WINDOW = "window" CONF_ACTIVE = "active" +CONF_CONTINUOUS = "continuous" +CONF_ON_SCAN_END = "on_scan_end" esp32_ble_tracker_ns = cg.esphome_ns.namespace("esp32_ble_tracker") ESP32BLETracker = esp32_ble_tracker_ns.class_("ESP32BLETracker", cg.Component) ESPBTClient = esp32_ble_tracker_ns.class_("ESPBTClient") @@ -42,6 +44,13 @@ BLEManufacturerDataAdvertiseTrigger = esp32_ble_tracker_ns.class_( "BLEManufacturerDataAdvertiseTrigger", automation.Trigger.template(adv_data_t_const_ref), ) +BLEEndOfScanTrigger = esp32_ble_tracker_ns.class_( + "BLEEndOfScanTrigger", automation.Trigger.template() +) +# Actions +ESP32BLEStartScanAction = esp32_ble_tracker_ns.class_( + "ESP32BLEStartScanAction", automation.Action +) def validate_scan_parameters(config): @@ -138,6 +147,7 @@ CONFIG_SCHEMA = cv.Schema( CONF_WINDOW, default="30ms" ): cv.positive_time_period_milliseconds, cv.Optional(CONF_ACTIVE, default=True): cv.boolean, + cv.Optional(CONF_CONTINUOUS, default=True): cv.boolean, } ), validate_scan_parameters, @@ -168,6 +178,9 @@ CONFIG_SCHEMA = cv.Schema( cv.Required(CONF_MANUFACTURER_ID): bt_uuid, } ), + cv.Optional(CONF_ON_SCAN_END): automation.validate_automation( + {cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(BLEEndOfScanTrigger)} + ), } ).extend(cv.COMPONENT_SCHEMA) @@ -186,6 +199,7 @@ async def to_code(config): cg.add(var.set_scan_interval(int(params[CONF_INTERVAL].total_milliseconds / 0.625))) cg.add(var.set_scan_window(int(params[CONF_WINDOW].total_milliseconds / 0.625))) cg.add(var.set_scan_active(params[CONF_ACTIVE])) + cg.add(var.set_scan_continuous(params[CONF_CONTINUOUS])) for conf in config.get(CONF_ON_BLE_ADVERTISE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) if CONF_MAC_ADDRESS in conf: @@ -215,11 +229,36 @@ async def to_code(config): if CONF_MAC_ADDRESS in conf: cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex)) await automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf) + for conf in config.get(CONF_ON_SCAN_END, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) if CORE.using_esp_idf: add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) +ESP32_BLE_START_SCAN_ACTION_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(ESP32BLETracker), + cv.Optional(CONF_CONTINUOUS, default=False): cv.templatable(cv.boolean), + } +) + + +@automation.register_action( + "esp32_ble_tracker.start_scan", + ESP32BLEStartScanAction, + ESP32_BLE_START_SCAN_ACTION_SCHEMA, +) +async def esp32_ble_tracker_start_scan_action_to_code( + config, action_id, template_arg, args +): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + cg.add(var.set_continuous(config[CONF_CONTINUOUS])) + return var + + async def register_ble_device(var, config): paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) cg.add(paren.register_listener(var)) diff --git a/esphome/components/esp32_ble_tracker/automation.h b/esphome/components/esp32_ble_tracker/automation.h index 3505e9c26d..d9e3b820af 100644 --- a/esphome/components/esp32_ble_tracker/automation.h +++ b/esphome/components/esp32_ble_tracker/automation.h @@ -76,6 +76,14 @@ class BLEManufacturerDataAdvertiseTrigger : public Trigger, ESPBTUUID uuid_; }; +class BLEEndOfScanTrigger : public Trigger<>, public ESPBTDeviceListener { + public: + explicit BLEEndOfScanTrigger(ESP32BLETracker *parent) { parent->register_listener(this); } + + bool parse_device(const ESPBTDevice &device) override { return false; } + void on_scan_end() override { this->trigger(); } +}; + } // namespace esp32_ble_tracker } // namespace esphome diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 82945dc771..743f246d3a 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -46,13 +46,15 @@ void ESP32BLETracker::setup() { global_esp32_ble_tracker = this; this->scan_result_lock_ = xSemaphoreCreateMutex(); this->scan_end_lock_ = xSemaphoreCreateMutex(); - + this->scanner_idle_ = true; if (!ESP32BLETracker::ble_setup()) { this->mark_failed(); return; } - global_esp32_ble_tracker->start_scan_(true); + if (this->scan_continuous_) { + global_esp32_ble_tracker->start_scan_(true); + } } void ESP32BLETracker::loop() { @@ -68,14 +70,25 @@ void ESP32BLETracker::loop() { ble_event = this->ble_events_.pop(); } + if (this->scanner_idle_) { + return; + } + bool connecting = false; for (auto *client : this->clients_) { if (client->state() == ClientState::CONNECTING || client->state() == ClientState::DISCOVERED) connecting = true; } + if (!connecting && xSemaphoreTake(this->scan_end_lock_, 0L)) { xSemaphoreGive(this->scan_end_lock_); - global_esp32_ble_tracker->start_scan_(false); + if (this->scan_continuous_) { + global_esp32_ble_tracker->start_scan_(false); + } else if (xSemaphoreTake(this->scan_end_lock_, 0L) && !this->scanner_idle_) { + xSemaphoreGive(this->scan_end_lock_); + global_esp32_ble_tracker->end_of_scan_(); + return; + } } if (xSemaphoreTake(this->scan_result_lock_, 5L / portTICK_PERIOD_MS)) { @@ -134,6 +147,15 @@ void ESP32BLETracker::loop() { } } +void ESP32BLETracker::start_scan() { + if (xSemaphoreTake(this->scan_end_lock_, 0L)) { + xSemaphoreGive(this->scan_end_lock_); + global_esp32_ble_tracker->start_scan_(true); + } else { + ESP_LOGW(TAG, "Scan requested when a scan is already in progress. Ignoring."); + } +} + bool ESP32BLETracker::ble_setup() { // Initialize non-volatile storage for the bluetooth controller esp_err_t err = nvs_flash_init(); @@ -225,6 +247,7 @@ void ESP32BLETracker::start_scan_(bool first) { listener->on_scan_end(); } this->already_discovered_.clear(); + this->scanner_idle_ = false; this->scan_params_.scan_type = this->scan_active_ ? BLE_SCAN_TYPE_ACTIVE : BLE_SCAN_TYPE_PASSIVE; this->scan_params_.own_addr_type = BLE_ADDR_TYPE_PUBLIC; this->scan_params_.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL; @@ -240,6 +263,22 @@ void ESP32BLETracker::start_scan_(bool first) { }); } +void ESP32BLETracker::end_of_scan_() { + if (!xSemaphoreTake(this->scan_end_lock_, 0L)) { + ESP_LOGW(TAG, "Cannot clean up end of scan!"); + return; + } + + ESP_LOGD(TAG, "End of scan."); + this->scanner_idle_ = true; + this->already_discovered_.clear(); + xSemaphoreGive(this->scan_end_lock_); + this->cancel_timeout("scan"); + + for (auto *listener : this->listeners_) + listener->on_scan_end(); +} + void ESP32BLETracker::register_client(ESPBTClient *client) { client->app_id = ++this->app_id_; this->clients_.push_back(client); @@ -719,7 +758,9 @@ void ESP32BLETracker::dump_config() { ESP_LOGCONFIG(TAG, " Scan Interval: %.1f ms", this->scan_interval_ * 0.625f); ESP_LOGCONFIG(TAG, " Scan Window: %.1f ms", this->scan_window_ * 0.625f); ESP_LOGCONFIG(TAG, " Scan Type: %s", this->scan_active_ ? "ACTIVE" : "PASSIVE"); + ESP_LOGCONFIG(TAG, " Continuous Scanning: %s", this->scan_continuous_ ? "True" : "False"); } + void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) { const uint64_t address = device.address_uint64(); for (auto &disc : this->already_discovered_) { diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 62fff30a20..1d2900af69 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/automation.h" #include "esphome/core/helpers.h" #include "queue.h" @@ -171,6 +172,7 @@ class ESP32BLETracker : public Component { void set_scan_interval(uint32_t scan_interval) { scan_interval_ = scan_interval; } void set_scan_window(uint32_t scan_window) { scan_window_ = scan_window; } void set_scan_active(bool scan_active) { scan_active_ = scan_active; } + void set_scan_continuous(bool scan_continuous) { scan_continuous_ = scan_continuous; } /// Setup the FreeRTOS task and the Bluetooth stack. void setup() override; @@ -188,11 +190,15 @@ class ESP32BLETracker : public Component { void print_bt_device_info(const ESPBTDevice &device); + void start_scan(); + protected: /// The FreeRTOS task managing the bluetooth interface. static bool ble_setup(); /// Start a single scan by setting up the parameters and doing some esp-idf calls. void start_scan_(bool first); + /// Called when a scan ends + void end_of_scan_(); /// Callback that will handle all GAP events and redistribute them to other callbacks. static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); void real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); @@ -221,7 +227,9 @@ class ESP32BLETracker : public Component { uint32_t scan_duration_; uint32_t scan_interval_; uint32_t scan_window_; + bool scan_continuous_; bool scan_active_; + bool scanner_idle_; SemaphoreHandle_t scan_result_lock_; SemaphoreHandle_t scan_end_lock_; size_t scan_result_index_{0}; @@ -235,6 +243,19 @@ class ESP32BLETracker : public Component { // NOLINTNEXTLINE extern ESP32BLETracker *global_esp32_ble_tracker; +template class ESP32BLEStartScanAction : public Action { + public: + ESP32BLEStartScanAction(ESP32BLETracker *parent) : parent_(parent) {} + TEMPLATABLE_VALUE(bool, continuous) + void play(Ts... x) override { + this->parent_->set_scan_continuous(this->continuous_.value(x...)); + this->parent_->start_scan(); + } + + protected: + ESP32BLETracker *parent_; +}; + } // namespace esp32_ble_tracker } // namespace esphome From b3cca5dcb63a2060921801d77854048f7ba1a844 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 14 Sep 2022 14:57:45 +1200 Subject: [PATCH 62/66] Add stop action for ble scanning (#3799) --- .../components/esp32_ble_tracker/__init__.py | 25 +++++++++++++++++++ .../components/esp32_ble_tracker/automation.h | 18 +++++++++++++ .../esp32_ble_tracker/esp32_ble_tracker.cpp | 7 ++++++ .../esp32_ble_tracker/esp32_ble_tracker.h | 14 +---------- 4 files changed, 51 insertions(+), 13 deletions(-) diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index db7fcc68ae..f70fea0639 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -51,6 +51,9 @@ BLEEndOfScanTrigger = esp32_ble_tracker_ns.class_( ESP32BLEStartScanAction = esp32_ble_tracker_ns.class_( "ESP32BLEStartScanAction", automation.Action ) +ESP32BLEStopScanAction = esp32_ble_tracker_ns.class_( + "ESP32BLEStopScanAction", automation.Action +) def validate_scan_parameters(config): @@ -259,6 +262,28 @@ async def esp32_ble_tracker_start_scan_action_to_code( return var +ESP32_BLE_STOP_SCAN_ACTION_SCHEMA = automation.maybe_simple_id( + cv.Schema( + { + cv.GenerateID(): cv.use_id(ESP32BLETracker), + } + ) +) + + +@automation.register_action( + "esp32_ble_tracker.stop_scan", + ESP32BLEStopScanAction, + ESP32_BLE_STOP_SCAN_ACTION_SCHEMA, +) +async def esp32_ble_tracker_stop_scan_action_to_code( + config, action_id, template_arg, args +): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var + + async def register_ble_device(var, config): paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) cg.add(paren.register_listener(var)) diff --git a/esphome/components/esp32_ble_tracker/automation.h b/esphome/components/esp32_ble_tracker/automation.h index d9e3b820af..6131d6ddf7 100644 --- a/esphome/components/esp32_ble_tracker/automation.h +++ b/esphome/components/esp32_ble_tracker/automation.h @@ -84,6 +84,24 @@ class BLEEndOfScanTrigger : public Trigger<>, public ESPBTDeviceListener { void on_scan_end() override { this->trigger(); } }; +template class ESP32BLEStartScanAction : public Action { + public: + ESP32BLEStartScanAction(ESP32BLETracker *parent) : parent_(parent) {} + TEMPLATABLE_VALUE(bool, continuous) + void play(Ts... x) override { + this->parent_->set_scan_continuous(this->continuous_.value(x...)); + this->parent_->start_scan(); + } + + protected: + ESP32BLETracker *parent_; +}; + +template class ESP32BLEStopScanAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->stop_scan(); } +}; + } // namespace esp32_ble_tracker } // namespace esphome diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 743f246d3a..68d88eb1e8 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -156,6 +156,13 @@ void ESP32BLETracker::start_scan() { } } +void ESP32BLETracker::stop_scan() { + ESP_LOGD(TAG, "Stopping scan."); + this->scan_continuous_ = false; + esp_ble_gap_stop_scanning(); + this->cancel_timeout("scan"); +} + bool ESP32BLETracker::ble_setup() { // Initialize non-volatile storage for the bluetooth controller esp_err_t err = nvs_flash_init(); diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 1d2900af69..29d0c81542 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -191,6 +191,7 @@ class ESP32BLETracker : public Component { void print_bt_device_info(const ESPBTDevice &device); void start_scan(); + void stop_scan(); protected: /// The FreeRTOS task managing the bluetooth interface. @@ -243,19 +244,6 @@ class ESP32BLETracker : public Component { // NOLINTNEXTLINE extern ESP32BLETracker *global_esp32_ble_tracker; -template class ESP32BLEStartScanAction : public Action { - public: - ESP32BLEStartScanAction(ESP32BLETracker *parent) : parent_(parent) {} - TEMPLATABLE_VALUE(bool, continuous) - void play(Ts... x) override { - this->parent_->set_scan_continuous(this->continuous_.value(x...)); - this->parent_->start_scan(); - } - - protected: - ESP32BLETracker *parent_; -}; - } // namespace esp32_ble_tracker } // namespace esphome From a5e3cd1a42a1068bc3fc915d8589a4e2a477d2ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Kom=C3=A1rek?= Date: Wed, 14 Sep 2022 05:22:59 +0200 Subject: [PATCH 63/66] Add Prometheus Service Discovery for online devices (#3788) --- esphome/dashboard/dashboard.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 889f5cc0bf..9a8f072237 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -644,6 +644,33 @@ def _ping_func(filename, address): return filename, rc == 0 +class PrometheusServiceDiscoveryHandler(BaseHandler): + @authenticated + def get(self): + entries = _list_dashboard_entries() + self.set_header("content-type", "application/json") + sd = [] + for entry in entries: + if entry.web_port is None: + continue + labels = { + "__meta_name": entry.name, + "__meta_esp_platform": entry.target_platform, + "__meta_esphome_version": entry.storage.esphome_version, + } + for integration in entry.storage.loaded_integrations: + labels[f"__meta_integration_{integration}"] = "true" + sd.append( + { + "targets": [ + f"{entry.address}:{entry.web_port}", + ], + "labels": labels, + } + ) + self.write(json.dumps(sd)) + + class MDNSStatusThread(threading.Thread): def run(self): global IMPORT_RESULT @@ -993,6 +1020,7 @@ def make_app(debug=get_bool_env(ENV_DEV)): (f"{rel}import", ImportRequestHandler), (f"{rel}secret_keys", SecretKeysRequestHandler), (f"{rel}rename", EsphomeRenameHandler), + (f"{rel}prometheus-sd", PrometheusServiceDiscoveryHandler), ], **app_settings, ) From f4b0917239b91059a5680450e6805ff6fed53a3a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 14 Sep 2022 16:49:20 +1200 Subject: [PATCH 64/66] Allow ble tracker to subscribe to ota start and stop the scanning (#3800) --- .../components/esp32_ble_tracker/__init__.py | 2 + .../esp32_ble_tracker/esp32_ble_tracker.cpp | 37 +++++++++++++------ esphome/components/ota/__init__.py | 1 + esphome/components/ota/ota_component.cpp | 4 ++ esphome/components/ota/ota_component.h | 3 ++ esphome/core/defines.h | 1 + 6 files changed, 36 insertions(+), 12 deletions(-) diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index f70fea0639..6d7868d4c5 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -239,6 +239,8 @@ async def to_code(config): if CORE.using_esp_idf: add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) + cg.add_define("USE_OTA_STATE_CALLBACK") # To be notified when an OTA update starts + ESP32_BLE_START_SCAN_ACTION_SCHEMA = cv.Schema( { diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 68d88eb1e8..f7e51a8ab3 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -1,10 +1,11 @@ #ifdef USE_ESP32 #include "esp32_ble_tracker.h" -#include "esphome/core/log.h" #include "esphome/core/application.h" -#include "esphome/core/helpers.h" +#include "esphome/core/defines.h" #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include #include @@ -15,6 +16,10 @@ #include #include +#ifdef USE_OTA +#include "esphome/components/ota/ota_component.h" +#endif + #ifdef USE_ARDUINO #include #endif @@ -52,8 +57,16 @@ void ESP32BLETracker::setup() { return; } +#ifdef USE_OTA + ota::global_ota_component->add_on_state_callback([this](ota::OTAState state, float progress, uint8_t error) { + if (state == ota::OTA_STARTED) { + this->stop_scan(); + } + }); +#endif + if (this->scan_continuous_) { - global_esp32_ble_tracker->start_scan_(true); + this->start_scan_(true); } } @@ -83,10 +96,10 @@ void ESP32BLETracker::loop() { if (!connecting && xSemaphoreTake(this->scan_end_lock_, 0L)) { xSemaphoreGive(this->scan_end_lock_); if (this->scan_continuous_) { - global_esp32_ble_tracker->start_scan_(false); + this->start_scan_(false); } else if (xSemaphoreTake(this->scan_end_lock_, 0L) && !this->scanner_idle_) { xSemaphoreGive(this->scan_end_lock_); - global_esp32_ble_tracker->end_of_scan_(); + this->end_of_scan_(); return; } } @@ -150,7 +163,7 @@ void ESP32BLETracker::loop() { void ESP32BLETracker::start_scan() { if (xSemaphoreTake(this->scan_end_lock_, 0L)) { xSemaphoreGive(this->scan_end_lock_); - global_esp32_ble_tracker->start_scan_(true); + this->start_scan_(true); } else { ESP_LOGW(TAG, "Scan requested when a scan is already in progress. Ignoring."); } @@ -299,21 +312,21 @@ void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_ga void ESP32BLETracker::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { switch (event) { case ESP_GAP_BLE_SCAN_RESULT_EVT: - global_esp32_ble_tracker->gap_scan_result_(param->scan_rst); + this->gap_scan_result_(param->scan_rst); break; case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: - global_esp32_ble_tracker->gap_scan_set_param_complete_(param->scan_param_cmpl); + this->gap_scan_set_param_complete_(param->scan_param_cmpl); break; case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: - global_esp32_ble_tracker->gap_scan_start_complete_(param->scan_start_cmpl); + this->gap_scan_start_complete_(param->scan_start_cmpl); break; case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: - global_esp32_ble_tracker->gap_scan_stop_complete_(param->scan_stop_cmpl); + this->gap_scan_stop_complete_(param->scan_stop_cmpl); break; default: break; } - for (auto *client : global_esp32_ble_tracker->clients_) { + for (auto *client : this->clients_) { client->gap_event_handler(event, param); } } @@ -351,7 +364,7 @@ void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_i void ESP32BLETracker::real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { - for (auto *client : global_esp32_ble_tracker->clients_) { + for (auto *client : this->clients_) { client->gattc_event_handler(event, gattc_if, param); } } diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index b3d3b7ad23..1bc4012ce2 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -78,6 +78,7 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) cg.add(var.set_port(config[CONF_PORT])) + cg.add_define("USE_OTA") if CONF_PASSWORD in config: cg.add(var.set_auth_password(config[CONF_PASSWORD])) cg.add_define("USE_OTA_PASSWORD") diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index 25c9f2e912..a02d64cd08 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -21,6 +21,8 @@ static const char *const TAG = "ota"; static const uint8_t OTA_VERSION_1_0 = 1; +OTAComponent *global_ota_component = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + std::unique_ptr make_ota_backend() { #ifdef USE_ARDUINO #ifdef USE_ESP8266 @@ -35,6 +37,8 @@ std::unique_ptr make_ota_backend() { #endif // USE_ESP_IDF } +OTAComponent::OTAComponent() { global_ota_component = this; } + void OTAComponent::setup() { server_ = socket::socket_ip(SOCK_STREAM, 0); if (server_ == nullptr) { diff --git a/esphome/components/ota/ota_component.h b/esphome/components/ota/ota_component.h index d178805168..9a1c92f727 100644 --- a/esphome/components/ota/ota_component.h +++ b/esphome/components/ota/ota_component.h @@ -41,6 +41,7 @@ enum OTAState { OTA_COMPLETED = 0, OTA_STARTED, OTA_IN_PROGRESS, OTA_ERROR }; /// OTAComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA. class OTAComponent : public Component { public: + OTAComponent(); #ifdef USE_OTA_PASSWORD void set_auth_password(const std::string &password) { password_ = password; } #endif // USE_OTA_PASSWORD @@ -103,5 +104,7 @@ class OTAComponent : public Component { #endif }; +extern OTAComponent *global_ota_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + } // namespace ota } // namespace esphome diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 90676c421e..cea53b8545 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -32,6 +32,7 @@ #define USE_MEDIA_PLAYER #define USE_MQTT #define USE_NUMBER +#define USE_OTA #define USE_OTA_PASSWORD #define USE_OTA_STATE_CALLBACK #define USE_POWER_SUPPLY From 6236db1a27d17ea1f0481a51fad13ebaf1528601 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 14 Sep 2022 06:51:20 +0200 Subject: [PATCH 65/66] Add uFire ISE sensor (#3789) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/ufire_ise/__init__.py | 1 + esphome/components/ufire_ise/sensor.py | 127 +++++++++++++++++ esphome/components/ufire_ise/ufire_ise.cpp | 153 +++++++++++++++++++++ esphome/components/ufire_ise/ufire_ise.h | 95 +++++++++++++ esphome/const.py | 2 + tests/test2.yaml | 5 + tests/test4.yaml | 6 + 8 files changed, 390 insertions(+) create mode 100644 esphome/components/ufire_ise/__init__.py create mode 100644 esphome/components/ufire_ise/sensor.py create mode 100644 esphome/components/ufire_ise/ufire_ise.cpp create mode 100644 esphome/components/ufire_ise/ufire_ise.h diff --git a/CODEOWNERS b/CODEOWNERS index 3a1087191c..69e30027a9 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -246,6 +246,7 @@ esphome/components/tuya/switch/* @jesserockz esphome/components/tuya/text_sensor/* @dentra esphome/components/uart/* @esphome/core esphome/components/ufire_ec/* @pvizeli +esphome/components/ufire_ise/* @pvizeli esphome/components/ultrasonic/* @OttoWinter esphome/components/version/* @esphome/core esphome/components/wake_on_lan/* @willwill2will54 diff --git a/esphome/components/ufire_ise/__init__.py b/esphome/components/ufire_ise/__init__.py new file mode 100644 index 0000000000..08f36c7934 --- /dev/null +++ b/esphome/components/ufire_ise/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@pvizeli"] diff --git a/esphome/components/ufire_ise/sensor.py b/esphome/components/ufire_ise/sensor.py new file mode 100644 index 0000000000..8f4359d6af --- /dev/null +++ b/esphome/components/ufire_ise/sensor.py @@ -0,0 +1,127 @@ +import esphome.codegen as cg +from esphome import automation +import esphome.config_validation as cv +from esphome.components import i2c, sensor +from esphome.const import ( + CONF_ID, + CONF_PH, + CONF_TEMPERATURE, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + UNIT_PH, +) + +DEPENDENCIES = ["i2c"] + +CONF_SOLUTION = "solution" +CONF_TEMPERATURE_SENSOR = "temperature_sensor" + +ufire_ise_ns = cg.esphome_ns.namespace("ufire_ise") +UFireISEComponent = ufire_ise_ns.class_( + "UFireISEComponent", cg.PollingComponent, i2c.I2CDevice +) + +# Actions +UFireISECalibrateProbeLowAction = ufire_ise_ns.class_( + "UFireISECalibrateProbeLowAction", automation.Action +) +UFireISECalibrateProbeHighAction = ufire_ise_ns.class_( + "UFireISECalibrateProbeHighAction", automation.Action +) +UFireISEResetAction = ufire_ise_ns.class_("UFireISEResetAction", automation.Action) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(UFireISEComponent), + cv.Exclusive(CONF_TEMPERATURE, "temperature"): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + accuracy_decimals=1, + ), + cv.Optional(CONF_PH): sensor.sensor_schema( + unit_of_measurement=UNIT_PH, + icon=ICON_EMPTY, + device_class=DEVICE_CLASS_EMPTY, + state_class=STATE_CLASS_MEASUREMENT, + accuracy_decimals=1, + ), + cv.Exclusive(CONF_TEMPERATURE_SENSOR, "temperature"): cv.use_id( + sensor.Sensor + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x3F)) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + if CONF_TEMPERATURE in config: + sens = await sensor.new_sensor(config[CONF_TEMPERATURE]) + cg.add(var.set_temperature_sensor(sens)) + + if CONF_PH in config: + sens = await sensor.new_sensor(config[CONF_PH]) + cg.add(var.set_ph_sensor(sens)) + + if CONF_TEMPERATURE_SENSOR in config: + sens = await cg.get_variable(config[CONF_TEMPERATURE_SENSOR]) + cg.add(var.set_temperature_sensor_external(sens)) + + await i2c.register_i2c_device(var, config) + + +UFIRE_ISE_CALIBRATE_PROBE_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(UFireISEComponent), + cv.Required(CONF_SOLUTION): cv.templatable(float), + } +) + + +@automation.register_action( + "ufire_ise.calibrate_probe_low", + UFireISECalibrateProbeLowAction, + UFIRE_ISE_CALIBRATE_PROBE_SCHEMA, +) +async def ufire_ise_calibrate_probe_low_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + template_ = await cg.templatable(config[CONF_SOLUTION], args, float) + cg.add(var.set_solution(template_)) + return var + + +@automation.register_action( + "ufire_ise.calibrate_probe_high", + UFireISECalibrateProbeHighAction, + UFIRE_ISE_CALIBRATE_PROBE_SCHEMA, +) +async def ufire_ise_calibrate_probe_high_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + template_ = await cg.templatable(config[CONF_SOLUTION], args, float) + cg.add(var.set_solution(template_)) + return var + + +UFIRE_ISE_RESET_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(UFireISEComponent)}) + + +@automation.register_action( + "ufire_ise.reset", + UFireISEResetAction, + UFIRE_ISE_RESET_SCHEMA, +) +async def ufire_ise_reset_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + return var diff --git a/esphome/components/ufire_ise/ufire_ise.cpp b/esphome/components/ufire_ise/ufire_ise.cpp new file mode 100644 index 0000000000..957e6f3299 --- /dev/null +++ b/esphome/components/ufire_ise/ufire_ise.cpp @@ -0,0 +1,153 @@ +#include "esphome/core/log.h" +#include "ufire_ise.h" + +#include + +namespace esphome { +namespace ufire_ise { + +static const char *const TAG = "ufire_ise"; + +void UFireISEComponent::setup() { + ESP_LOGCONFIG(TAG, "Setting up uFire_ise..."); + + uint8_t version; + if (!this->read_byte(REGISTER_VERSION, &version) && version != 0xFF) { + this->mark_failed(); + return; + } + ESP_LOGI(TAG, "Found uFire_ise board version 0x%02X", version); + + // Write option for temperature adjustments + uint8_t config; + this->read_byte(REGISTER_CONFIG, &config); + if (this->temperature_sensor_ == nullptr && this->temperature_sensor_external_ == nullptr) { + config &= ~CONFIG_TEMP_COMPENSATION; + } else { + config |= CONFIG_TEMP_COMPENSATION; + } + this->write_byte(REGISTER_CONFIG, config); +} + +void UFireISEComponent::update() { + int wait = 0; + if (this->temperature_sensor_ != nullptr) { + this->write_byte(REGISTER_TASK, COMMAND_MEASURE_TEMP); + wait += 750; + } + if (this->ph_sensor_ != nullptr) { + this->write_byte(REGISTER_TASK, COMMAND_MEASURE_MV); + wait += 750; + } + + // Wait until measurement are taken + this->set_timeout("data", wait, [this]() { this->update_internal_(); }); +} + +void UFireISEComponent::update_internal_() { + float temperature = 0; + + // Read temperature internal and populate it + if (this->temperature_sensor_ != nullptr) { + temperature = this->measure_temperature_(); + this->temperature_sensor_->publish_state(temperature); + } + // Get temperature from external only for adjustments + else if (this->temperature_sensor_external_ != nullptr) { + temperature = this->temperature_sensor_external_->state; + } + + if (this->ph_sensor_ != nullptr) { + this->ph_sensor_->publish_state(this->measure_ph_(temperature)); + } +} + +float UFireISEComponent::measure_temperature_() { return this->read_data_(REGISTER_TEMP); } + +float UFireISEComponent::measure_mv_() { return this->read_data_(REGISTER_MV); } + +float UFireISEComponent::measure_ph_(float temperature) { + float mv, ph; + + mv = this->measure_mv_(); + if (mv == -1) + return -1; + + ph = fabs(7.0 - (mv / PROBE_MV_TO_PH)); + + // Determine the temperature correction + float distance_from_7 = std::abs(7 - roundf(ph)); + float distance_from_25 = std::floor(std::abs(25 - roundf(temperature)) / 10); + float temp_multiplier = (distance_from_25 * distance_from_7) * PROBE_TMP_CORRECTION; + if ((ph >= 8.0) && (temperature >= 35)) + temp_multiplier *= -1; + if ((ph <= 6.0) && (temperature <= 15)) + temp_multiplier *= -1; + + ph += temp_multiplier; + if ((ph <= 0.0) || (ph > 14.0)) + ph = -1; + if (std::isinf(ph)) + ph = -1; + if (std::isnan(ph)) + ph = -1; + + return ph; +} + +void UFireISEComponent::set_solution_(float solution) { + solution = (7 - solution) * PROBE_MV_TO_PH; + this->write_data_(REGISTER_SOLUTION, solution); +} + +void UFireISEComponent::calibrate_probe_low(float solution) { + this->set_solution_(solution); + this->write_byte(REGISTER_TASK, COMMAND_CALIBRATE_LOW); +} + +void UFireISEComponent::calibrate_probe_high(float solution) { + this->set_solution_(solution); + this->write_byte(REGISTER_TASK, COMMAND_CALIBRATE_HIGH); +} + +void UFireISEComponent::reset_board() { + this->write_data_(REGISTER_REFHIGH, NAN); + this->write_data_(REGISTER_REFLOW, NAN); + this->write_data_(REGISTER_READHIGH, NAN); + this->write_data_(REGISTER_READLOW, NAN); +} + +float UFireISEComponent::read_data_(uint8_t reg) { + float f; + uint8_t temp[4]; + + this->write(®, 1); + delay(10); + + for (uint8_t i = 0; i < 4; i++) { + this->read_bytes_raw(temp + i, 1); + } + memcpy(&f, temp, sizeof(f)); + + return f; +} + +void UFireISEComponent::write_data_(uint8_t reg, float data) { + uint8_t temp[4]; + + memcpy(temp, &data, sizeof(data)); + this->write_bytes(reg, temp, 4); + delay(10); +} + +void UFireISEComponent::dump_config() { + ESP_LOGCONFIG(TAG, "uFire-ISE"); + LOG_I2C_DEVICE(this) + LOG_UPDATE_INTERVAL(this) + LOG_SENSOR(" ", "PH Sensor", this->ph_sensor_) + LOG_SENSOR(" ", "Temperature Sensor", this->temperature_sensor_) + LOG_SENSOR(" ", "Temperature Sensor external", this->temperature_sensor_external_) +} + +} // namespace ufire_ise +} // namespace esphome diff --git a/esphome/components/ufire_ise/ufire_ise.h b/esphome/components/ufire_ise/ufire_ise.h new file mode 100644 index 0000000000..01efdcdb55 --- /dev/null +++ b/esphome/components/ufire_ise/ufire_ise.h @@ -0,0 +1,95 @@ +#pragma once + +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace ufire_ise { + +static const float PROBE_MV_TO_PH = 59.2; +static const float PROBE_TMP_CORRECTION = 0.03; + +static const uint8_t CONFIG_TEMP_COMPENSATION = 0x02; + +static const uint8_t REGISTER_VERSION = 0; +static const uint8_t REGISTER_MV = 1; +static const uint8_t REGISTER_TEMP = 5; +static const uint8_t REGISTER_REFHIGH = 13; +static const uint8_t REGISTER_REFLOW = 17; +static const uint8_t REGISTER_READHIGH = 21; +static const uint8_t REGISTER_READLOW = 25; +static const uint8_t REGISTER_SOLUTION = 29; +static const uint8_t REGISTER_CONFIG = 38; +static const uint8_t REGISTER_TASK = 39; + +static const uint8_t COMMAND_CALIBRATE_HIGH = 8; +static const uint8_t COMMAND_CALIBRATE_LOW = 10; +static const uint8_t COMMAND_MEASURE_TEMP = 40; +static const uint8_t COMMAND_MEASURE_MV = 80; + +class UFireISEComponent : public PollingComponent, public i2c::I2CDevice { + public: + void setup() override; + void update() override; + void dump_config() override; + + void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; } + void set_temperature_sensor_external(sensor::Sensor *temperature_sensor) { + this->temperature_sensor_external_ = temperature_sensor; + } + void set_ph_sensor(sensor::Sensor *ph_sensor) { this->ph_sensor_ = ph_sensor; } + void calibrate_probe_low(float solution); + void calibrate_probe_high(float solution); + void reset_board(); + + protected: + float measure_temperature_(); + float measure_mv_(); + float measure_ph_(float temperature); + void set_solution_(float solution); + float read_data_(uint8_t reg); + void write_data_(uint8_t reg, float data); + void update_internal_(); + + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *temperature_sensor_external_{nullptr}; + sensor::Sensor *ph_sensor_{nullptr}; +}; + +template class UFireISECalibrateProbeLowAction : public Action { + public: + UFireISECalibrateProbeLowAction(UFireISEComponent *parent) : parent_(parent) {} + TEMPLATABLE_VALUE(float, solution) + + void play(Ts... x) override { this->parent_->calibrate_probe_low(this->solution_.value(x...)); } + + protected: + UFireISEComponent *parent_; +}; + +template class UFireISECalibrateProbeHighAction : public Action { + public: + UFireISECalibrateProbeHighAction(UFireISEComponent *parent) : parent_(parent) {} + TEMPLATABLE_VALUE(float, solution) + + void play(Ts... x) override { this->parent_->calibrate_probe_high(this->solution_.value(x...)); } + + protected: + UFireISEComponent *parent_; +}; + +template class UFireISEResetAction : public Action { + public: + UFireISEResetAction(UFireISEComponent *parent) : parent_(parent) {} + + void play(Ts... x) override { this->parent_->reset_board(); } + + protected: + UFireISEComponent *parent_; +}; + +} // namespace ufire_ise +} // namespace esphome diff --git a/esphome/const.py b/esphome/const.py index 5153efbbe0..c9d46fc82c 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -493,6 +493,7 @@ CONF_PAYLOAD = "payload" CONF_PAYLOAD_AVAILABLE = "payload_available" CONF_PAYLOAD_NOT_AVAILABLE = "payload_not_available" CONF_PERIOD = "period" +CONF_PH = "ph" CONF_PHASE_ANGLE = "phase_angle" CONF_PHASE_BALANCER = "phase_balancer" CONF_PIN = "pin" @@ -881,6 +882,7 @@ UNIT_PARTS_PER_BILLION = "ppb" UNIT_PARTS_PER_MILLION = "ppm" UNIT_PASCAL = "Pa" UNIT_PERCENT = "%" +UNIT_PH = "pH" UNIT_PULSES = "pulses" UNIT_PULSES_PER_MINUTE = "pulses/min" UNIT_SECOND = "s" diff --git a/tests/test2.yaml b/tests/test2.yaml index f8ed04d389..5507fa0631 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -376,6 +376,11 @@ sensor: temperature_sensor: ha_hello_world_temperature temperature_compensation: 20.0 temperature_coefficient: 0.019 + - platform: ufire_ise + id: ufire_ise_board + temperature_sensor: ha_hello_world_temperature + ph: + name: Ufire pH time: - platform: homeassistant diff --git a/tests/test4.yaml b/tests/test4.yaml index d79b421cf8..6293e0f7b7 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -251,6 +251,12 @@ sensor: name: Ufire EC temperature_compensation: 20.0 temperature_coefficient: 0.019 + - platform: ufire_ise + id: ufire_ise_board + temperature: + name: Ufire Temperature + ph: + name: Ufire pH # # platform sensor.apds9960 requires component apds9960 From f1e8cc2cf091fe6a3c857d802a3d5d391c1d4886 Mon Sep 17 00:00:00 2001 From: Ignacio Hernandez-Ros Date: Wed, 14 Sep 2022 06:53:51 +0200 Subject: [PATCH 66/66] fix spi timing issues (#3763) --- esphome/components/spi/spi.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index 7f0b0f481a..6c92321ac8 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -195,6 +195,11 @@ class SPIComponent : public Component { template void enable(GPIOPin *cs) { + if (cs != nullptr) { + this->active_cs_ = cs; + this->active_cs_->digital_write(false); + } + #ifdef USE_SPI_ARDUINO_BACKEND if (this->hw_spi_ != nullptr) { uint8_t data_mode = SPI_MODE0; @@ -215,11 +220,6 @@ class SPIComponent : public Component { #ifdef USE_SPI_ARDUINO_BACKEND } #endif // USE_SPI_ARDUINO_BACKEND - - if (cs != nullptr) { - this->active_cs_ = cs; - this->active_cs_->digital_write(false); - } } void disable();