diff --git a/interface/src/project/EMSESPDevicesController.tsx b/interface/src/project/EMSESPDevicesController.tsx index 52945963a..dfcb5bf1c 100644 --- a/interface/src/project/EMSESPDevicesController.tsx +++ b/interface/src/project/EMSESPDevicesController.tsx @@ -5,7 +5,7 @@ import { ENDPOINT_ROOT } from '../api'; import EMSESPDevicesForm from './EMSESPDevicesForm'; import { EMSESPDevices } from './EMSESPtypes'; -export const EMSESP_DEVICES_ENDPOINT = ENDPOINT_ROOT + "emsespDevices"; +export const EMSESP_DEVICES_ENDPOINT = ENDPOINT_ROOT + "allDevices"; type EMSESPDevicesControllerProps = RestControllerProps; diff --git a/interface/src/project/EMSESPDevicesForm.tsx b/interface/src/project/EMSESPDevicesForm.tsx index 41baa437d..2f6c2b83a 100644 --- a/interface/src/project/EMSESPDevicesForm.tsx +++ b/interface/src/project/EMSESPDevicesForm.tsx @@ -24,10 +24,11 @@ import { FormButton, } from "../components"; -import { EMSESPDevices, Device } from "./EMSESPtypes"; +import { EMSESPDevices, EMSESPDeviceData, Device } from "./EMSESPtypes"; import { ENDPOINT_ROOT } from '../api'; export const SCANDEVICES_ENDPOINT = ENDPOINT_ROOT + "scanDevices"; +export const DEVICE_DATA_ENDPOINT = ENDPOINT_ROOT + "deviceData"; const StyledTableCell = withStyles((theme: Theme) => createStyles({ @@ -54,6 +55,7 @@ function compareDevices(a: Device, b: Device) { interface EMSESPDevicesFormState { confirmScanDevices: boolean; processing: boolean; + deviceData?: EMSESPDeviceData; } type EMSESPDevicesFormProps = RestFormProps & AuthenticatedContextProps & WithWidthProps; @@ -65,15 +67,19 @@ class EMSESPDevicesForm extends Component { + noDevices = () => { return (this.props.data.devices.length === 0); }; + noDeviceData = () => { + return (this.state.deviceData?.deviceData.length === 0); + }; + createTableItems() { const { width, data } = this.props; return ( - {!this.noData() && ( + {!this.noDevices() && ( @@ -87,7 +93,9 @@ class EMSESPDevicesForm extends Component {data.devices.sort(compareDevices).map(device => ( - + this.handleRowClick(device.id)} + > {device.type} @@ -111,7 +119,7 @@ class EMSESPDevicesForm extends Component
)} - {this.noData() && + {this.noDevices() && ( @@ -156,26 +164,105 @@ class EMSESPDevicesForm extends Component { this.setState({ processing: true }); - redirectingAuthorizedFetch(SCANDEVICES_ENDPOINT, { method: 'POST' }) - .then(response => { - if (response.status === 200) { - this.props.enqueueSnackbar("Device scan is starting...", { variant: 'info' }); - this.setState({ processing: false, confirmScanDevices: false }); - } else { - throw Error("Invalid status code: " + response.status); - } - }) + redirectingAuthorizedFetch(SCANDEVICES_ENDPOINT).then(response => { + if (response.status === 200) { + this.props.enqueueSnackbar("Device scan is starting...", { variant: 'info' }); + this.setState({ processing: false, confirmScanDevices: false }); + } else { + throw Error("Invalid status code: " + response.status); + } + }) .catch(error => { this.props.enqueueSnackbar(error.message || "Problem with scan", { variant: 'error' }); this.setState({ processing: false, confirmScanDevices: false }); }); } + handleRowClick = (id: any) => { + this.setState({ deviceData: undefined }); + redirectingAuthorizedFetch(DEVICE_DATA_ENDPOINT, { + method: 'POST', + body: JSON.stringify({ id: id }), + headers: { + 'Content-Type': 'application/json' + } + }).then(response => { + if (response.status === 200) { + return response.json(); + // this.setState({ errorMessage: undefined }, this.props.loadData); + } + throw Error("Unexpected response code: " + response.status); + }).then(json => { + this.setState({ deviceData: json }); + }).catch(error => { + this.props.enqueueSnackbar(error.message || "Problem getting device data", { variant: 'error' }); + this.setState({ deviceData: undefined }); + }); + } + + renderDeviceData() { + const { deviceData } = this.state; + const { width } = this.props; + + if (this.noDevices()) { + return ( +

+ ) + } + + if (!deviceData) { + return ( + +

+ Click on a device to show it's values +
+ ); + } + + if ((deviceData.deviceData || []).length === 0) { + return ( +

+ ); + } + + return ( + +

+ + + {deviceData.deviceName} + + + + + + + + + {deviceData.deviceData.map(deviceData => ( + + + {deviceData.name} + + + {deviceData.value} + + + ))} + +
+
+ + ); + + } + render() { return (

{this.createTableItems()} + {this.renderDeviceData()}

diff --git a/interface/src/project/EMSESPtypes.ts b/interface/src/project/EMSESPtypes.ts index 91fed67ee..2e510ab4e 100644 --- a/interface/src/project/EMSESPtypes.ts +++ b/interface/src/project/EMSESPtypes.ts @@ -24,6 +24,7 @@ export interface EMSESPStatus { } export interface Device { + id: number; type: string; brand: string; name: string; @@ -36,3 +37,13 @@ export interface EMSESPDevices { devices: Device[]; } +export interface DeviceData { + name: string; + value: string; +} + +export interface EMSESPDeviceData { + deviceName: string; + deviceData: DeviceData[]; +} + diff --git a/media/web_devices.PNG b/media/web_devices.PNG index 684968967..4a9796a7b 100644 Binary files a/media/web_devices.PNG and b/media/web_devices.PNG differ diff --git a/src/EMSESPDevicesService.cpp b/src/EMSESPDevicesService.cpp index 1ce990286..585b2b4c8 100644 --- a/src/EMSESPDevicesService.cpp +++ b/src/EMSESPDevicesService.cpp @@ -4,22 +4,37 @@ namespace emsesp { -EMSESPDevicesService::EMSESPDevicesService(AsyncWebServer * server, SecurityManager * securityManager) { +EMSESPDevicesService::EMSESPDevicesService(AsyncWebServer * server, SecurityManager * securityManager) + : _timeHandler(DEVICE_DATA_SERVICE_PATH, + securityManager->wrapCallback(std::bind(&EMSESPDevicesService::device_data, this, _1, _2), AuthenticationPredicates::IS_AUTHENTICATED)) { + // body server->on(EMSESP_DEVICES_SERVICE_PATH, HTTP_GET, - securityManager->wrapRequest(std::bind(&EMSESPDevicesService::emsespDevicesService, this, std::placeholders::_1), - AuthenticationPredicates::IS_AUTHENTICATED)); + securityManager->wrapRequest(std::bind(&EMSESPDevicesService::all_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); + + server->on(SCAN_DEVICES_SERVICE_PATH, + HTTP_GET, + securityManager->wrapRequest(std::bind(&EMSESPDevicesService::scan_devices, this, _1), AuthenticationPredicates::IS_AUTHENTICATED)); + + _timeHandler.setMethod(HTTP_POST); + _timeHandler.setMaxContentLength(256); + server->addHandler(&_timeHandler); } -void EMSESPDevicesService::emsespDevicesService(AsyncWebServerRequest * request) { +void EMSESPDevicesService::scan_devices(AsyncWebServerRequest * request) { + EMSESP::send_read_request(EMSdevice::EMS_TYPE_UBADevices, EMSdevice::EMS_DEVICE_ID_BOILER); + request->send(200); +} + +void EMSESPDevicesService::all_devices(AsyncWebServerRequest * request) { AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_EMSESP_STATUS_SIZE); JsonObject root = response->getRoot(); JsonArray devices = root.createNestedArray("devices"); - for (const auto & emsdevice : EMSESP::emsdevices) { if (emsdevice) { JsonObject deviceRoot = devices.createNestedObject(); + deviceRoot["id"] = emsdevice->unique_id(); deviceRoot["type"] = emsdevice->device_type_name(); deviceRoot["brand"] = emsdevice->brand_to_string(); deviceRoot["name"] = emsdevice->name(); @@ -28,8 +43,23 @@ void EMSESPDevicesService::emsespDevicesService(AsyncWebServerRequest * request) deviceRoot["version"] = emsdevice->version(); } } + response->setLength(); request->send(response); } +void EMSESPDevicesService::device_data(AsyncWebServerRequest * request, JsonVariant & json) { + if (json.is()) { + uint8_t id = json["id"]; // get id from selected table row + + AsyncJsonResponse * response = new AsyncJsonResponse(false, 1024); + EMSESP::device_info(id, (JsonObject &)response->getRoot()); + response->setLength(); + request->send(response); + } else { + AsyncWebServerResponse * response = request->beginResponse(200); + request->send(response); + } +} + } // namespace emsesp \ No newline at end of file diff --git a/src/EMSESPDevicesService.h b/src/EMSESPDevicesService.h index 02f7187f1..db242984e 100644 --- a/src/EMSESPDevicesService.h +++ b/src/EMSESPDevicesService.h @@ -6,23 +6,26 @@ #include #include -// #include -// #include -// #include - -#include "version.h" - #define MAX_EMSESP_STATUS_SIZE 1024 -#define EMSESP_DEVICES_SERVICE_PATH "/rest/emsespDevices" + +#define EMSESP_DEVICES_SERVICE_PATH "/rest/allDevices" +#define SCAN_DEVICES_SERVICE_PATH "/rest/scanDevices" +#define DEVICE_DATA_SERVICE_PATH "/rest/deviceData" namespace emsesp { +using namespace std::placeholders; // for `_1` + class EMSESPDevicesService { public: EMSESPDevicesService(AsyncWebServer * server, SecurityManager * securityManager); private: - void emsespDevicesService(AsyncWebServerRequest * request); + void all_devices(AsyncWebServerRequest * request); + void scan_devices(AsyncWebServerRequest * request); + + AsyncCallbackJsonWebHandler _timeHandler; + void device_data(AsyncWebServerRequest * request, JsonVariant & json); }; } // namespace emsesp diff --git a/src/devices/boiler.cpp b/src/devices/boiler.cpp index 44b72d7ba..637c60bba 100644 --- a/src/devices/boiler.cpp +++ b/src/devices/boiler.cpp @@ -166,6 +166,24 @@ void Boiler::boiler_cmd_wwtemp(const char * message) { } } +void Boiler::device_info(JsonArray & root) { + JsonObject dataElement; + + dataElement = root.createNestedObject(); + dataElement["name"] = F("Hot tap water"); + dataElement["value"] = tap_water_active_ ? F("running") : F("off"); + + dataElement = root.createNestedObject(); + dataElement["name"] = F("Central heating"); + dataElement["value"] = heating_active_ ? F("active") : F("off"); + + render_value_json(root, "", F("Selected flow temperature"), selFlowTemp_, F_(degrees)); + render_value_json(root, "", F("Current flow temperature"), curFlowTemp_, F_(degrees), 10); + render_value_json(root, "", F("Warm Water selected temperature"), wWSelTemp_, F_(degrees)); + render_value_json(root, "", F("Warm Water set temperature"), wWSetTmp_, F_(degrees)); + render_value_json(root, "", F("Warm Water current temperature (intern)"), wWCurTmp_, F_(degrees), 10); +} + // publish values via MQTT void Boiler::publish_values() { const size_t capacity = JSON_OBJECT_SIZE(47); // must recalculate if more objects addded https://arduinojson.org/v6/assistant/ diff --git a/src/devices/boiler.h b/src/devices/boiler.h index 41f7e8160..60e4170e1 100644 --- a/src/devices/boiler.h +++ b/src/devices/boiler.h @@ -40,6 +40,7 @@ class Boiler : public EMSdevice { virtual void show_values(uuid::console::Shell & shell); virtual void publish_values(); + virtual void device_info(JsonArray & root); virtual bool updated_values(); virtual void add_context_menu(); diff --git a/src/devices/connect.cpp b/src/devices/connect.cpp index 07fb346c7..d8da0d612 100644 --- a/src/devices/connect.cpp +++ b/src/devices/connect.cpp @@ -34,6 +34,9 @@ Connect::Connect(uint8_t device_type, uint8_t device_id, uint8_t product_id, con // register_mqtt_topic("topic", std::bind(&Connect::cmd, this, _1)); } +void Connect::device_info(JsonArray & root) { +} + void Connect::add_context_menu() { } diff --git a/src/devices/connect.h b/src/devices/connect.h index 37bf8a7e9..51956ef5a 100644 --- a/src/devices/connect.h +++ b/src/devices/connect.h @@ -37,6 +37,7 @@ class Connect : public EMSdevice { virtual void show_values(uuid::console::Shell & shell); virtual void publish_values(); + virtual void device_info(JsonArray & root); virtual bool updated_values(); virtual void add_context_menu(); diff --git a/src/devices/controller.cpp b/src/devices/controller.cpp index c7d5727e9..a6796ce8b 100644 --- a/src/devices/controller.cpp +++ b/src/devices/controller.cpp @@ -39,6 +39,9 @@ Controller::Controller(uint8_t device_type, uint8_t device_id, uint8_t product_i void Controller::add_context_menu() { } +void Controller::device_info(JsonArray & root) { +} + // display all values into the shell console void Controller::show_values(uuid::console::Shell & shell) { EMSdevice::show_values(shell); // always call this to show header diff --git a/src/devices/controller.h b/src/devices/controller.h index 36866a8f7..d905b3864 100644 --- a/src/devices/controller.h +++ b/src/devices/controller.h @@ -37,6 +37,7 @@ class Controller : public EMSdevice { virtual void show_values(uuid::console::Shell & shell); virtual void publish_values(); + virtual void device_info(JsonArray & root); virtual bool updated_values(); virtual void add_context_menu(); diff --git a/src/devices/gateway.cpp b/src/devices/gateway.cpp index 101791f1d..aa8bec0bf 100644 --- a/src/devices/gateway.cpp +++ b/src/devices/gateway.cpp @@ -39,6 +39,9 @@ Gateway::Gateway(uint8_t device_type, uint8_t device_id, uint8_t product_id, con void Gateway::add_context_menu() { } +void Gateway::device_info(JsonArray & root) { +} + // display all values into the shell console void Gateway::show_values(uuid::console::Shell & shell) { EMSdevice::show_values(shell); // always call this to show header diff --git a/src/devices/gateway.h b/src/devices/gateway.h index e27eff894..d38dbb32e 100644 --- a/src/devices/gateway.h +++ b/src/devices/gateway.h @@ -37,6 +37,7 @@ class Gateway : public EMSdevice { virtual void show_values(uuid::console::Shell & shell); virtual void publish_values(); + virtual void device_info(JsonArray & root); virtual bool updated_values(); virtual void add_context_menu(); diff --git a/src/devices/heatpump.cpp b/src/devices/heatpump.cpp index fec97a61d..14b8e74ea 100644 --- a/src/devices/heatpump.cpp +++ b/src/devices/heatpump.cpp @@ -50,6 +50,9 @@ Heatpump::Heatpump(uint8_t device_type, uint8_t device_id, uint8_t product_id, c void Heatpump::add_context_menu() { } +void Heatpump::device_info(JsonArray & root) { +} + // display all values into the shell console void Heatpump::show_values(uuid::console::Shell & shell) { EMSdevice::show_values(shell); // always call this to show header diff --git a/src/devices/heatpump.h b/src/devices/heatpump.h index dda5acd3c..a23921698 100644 --- a/src/devices/heatpump.h +++ b/src/devices/heatpump.h @@ -37,6 +37,7 @@ class Heatpump : public EMSdevice { virtual void show_values(uuid::console::Shell & shell); virtual void publish_values(); + virtual void device_info(JsonArray & root); virtual bool updated_values(); virtual void add_context_menu(); diff --git a/src/devices/mixing.cpp b/src/devices/mixing.cpp index 52938aff7..c4ebe6ced 100644 --- a/src/devices/mixing.cpp +++ b/src/devices/mixing.cpp @@ -57,6 +57,9 @@ Mixing::Mixing(uint8_t device_type, uint8_t device_id, uint8_t product_id, const void Mixing::add_context_menu() { } +void Mixing::device_info(JsonArray & root) { +} + // check to see if values have been updated bool Mixing::updated_values() { return false; diff --git a/src/devices/mixing.h b/src/devices/mixing.h index ed1e8ce39..d1c6b09b9 100644 --- a/src/devices/mixing.h +++ b/src/devices/mixing.h @@ -37,6 +37,7 @@ class Mixing : public EMSdevice { virtual void show_values(uuid::console::Shell & shell); virtual void publish_values(); + virtual void device_info(JsonArray & root); virtual bool updated_values(); virtual void add_context_menu(); diff --git a/src/devices/solar.cpp b/src/devices/solar.cpp index feb2987c0..d3e9e0c09 100644 --- a/src/devices/solar.cpp +++ b/src/devices/solar.cpp @@ -58,6 +58,9 @@ Solar::Solar(uint8_t device_type, uint8_t device_id, uint8_t product_id, const s void Solar::add_context_menu() { } +void Solar::device_info(JsonArray & root) { +} + // display all values into the shell console void Solar::show_values(uuid::console::Shell & shell) { EMSdevice::show_values(shell); // always call this to show header diff --git a/src/devices/solar.h b/src/devices/solar.h index 5a1be5e5b..55b3e398a 100644 --- a/src/devices/solar.h +++ b/src/devices/solar.h @@ -37,6 +37,7 @@ class Solar : public EMSdevice { virtual void show_values(uuid::console::Shell & shell); virtual void publish_values(); + virtual void device_info(JsonArray & root); virtual bool updated_values(); virtual void add_context_menu(); diff --git a/src/devices/switch.cpp b/src/devices/switch.cpp index f86858fff..c7a160260 100644 --- a/src/devices/switch.cpp +++ b/src/devices/switch.cpp @@ -39,6 +39,9 @@ Switch::Switch(uint8_t device_type, uint8_t device_id, uint8_t product_id, const void Switch::add_context_menu() { } +void Switch::device_info(JsonArray & root) { +} + // display all values into the shell console void Switch::show_values(uuid::console::Shell & shell) { EMSdevice::show_values(shell); // always call this to show header diff --git a/src/devices/switch.h b/src/devices/switch.h index cf74014eb..44d9844d6 100644 --- a/src/devices/switch.h +++ b/src/devices/switch.h @@ -37,6 +37,7 @@ class Switch : public EMSdevice { virtual void show_values(uuid::console::Shell & shell); virtual void publish_values(); + virtual void device_info(JsonArray & root); virtual bool updated_values(); virtual void add_context_menu(); diff --git a/src/devices/thermostat.cpp b/src/devices/thermostat.cpp index 9c52e0760..2978bab35 100644 --- a/src/devices/thermostat.cpp +++ b/src/devices/thermostat.cpp @@ -169,6 +169,50 @@ void Thermostat::init_mqtt() { register_mqtt_topic("thermostat_cmd_mode", std::bind(&Thermostat::thermostat_cmd_mode, this, _1)); } +// prepare data for Web UI +void Thermostat::device_info(JsonArray & root) { + JsonObject dataElement; + + uint8_t flags = (this->flags() & 0x0F); // specific thermostat characteristics, strip the option bits + + for (const auto & hc : heating_circuits_) { + if (!Helpers::hasValue(hc->setpoint_roomTemp)) { + break; // skip this HC + } + + // different thermostat types store their temperature values differently + uint8_t format_setpoint, format_curr; + switch (flags) { + case EMS_DEVICE_FLAG_EASY: + format_setpoint = 100; // *100 + format_curr = 100; // *100 + break; + case EMS_DEVICE_FLAG_JUNKERS: + format_setpoint = 10; // *10 + format_curr = 10; // *10 + break; + default: // RC30, RC35 etc... + format_setpoint = 2; // *2 + format_curr = 10; // *10 + break; + } + + // create prefix with heating circuit number + std::string hc_str(5, '\0'); + snprintf_P(&hc_str[0], hc_str.capacity() + 1, PSTR("hc%d: "), hc->hc_num()); + + render_value_json(root, hc_str, F("Current room temperature"), hc->curr_roomTemp, F_(degrees), format_curr); + render_value_json(root, hc_str, F("Setpoint room temperature"), hc->setpoint_roomTemp, F_(degrees), format_setpoint); + if (Helpers::hasValue(hc->mode)) { + dataElement = root.createNestedObject(); + std::string mode_str(15, '\0'); + snprintf_P(&mode_str[0], mode_str.capacity() + 1, PSTR("%sMode"), hc_str.c_str()); + dataElement["name"] = mode_str; + dataElement["value"] = mode_tostring(hc->get_mode(flags)); + } + } +} + // only add the menu for the master thermostat void Thermostat::add_context_menu() { if (device_id() != EMSESP::actual_master_thermostat()) { @@ -1024,8 +1068,6 @@ void Thermostat::show_values(uuid::console::Shell & shell) { } } - // std::sort(heating_circuits_.begin(), heating_circuits_.end()); // sort based on hc number. This has moved to the heating_circuit() function - for (const auto & hc : heating_circuits_) { if (!Helpers::hasValue(hc->setpoint_roomTemp)) { break; // skip this HC diff --git a/src/devices/thermostat.h b/src/devices/thermostat.h index 8f2ea0c76..ee019b87b 100644 --- a/src/devices/thermostat.h +++ b/src/devices/thermostat.h @@ -94,6 +94,7 @@ class Thermostat : public EMSdevice { virtual void show_values(uuid::console::Shell & shell); virtual void publish_values(); + virtual void device_info(JsonArray & root); virtual bool updated_values(); virtual void add_context_menu(); diff --git a/src/emsdevice.cpp b/src/emsdevice.cpp index 16341f8ad..2185b17df 100644 --- a/src/emsdevice.cpp +++ b/src/emsdevice.cpp @@ -127,7 +127,7 @@ uint8_t EMSdevice::decode_brand(uint8_t value) { } } -// print human friendly description of the EMS device +// returns string of a human friendly description of the EMS device std::string EMSdevice::to_string() const { std::string str(160, '\0'); @@ -153,6 +153,17 @@ std::string EMSdevice::to_string() const { return str; } +// returns out brand + device name +std::string EMSdevice::to_string_short() const { + std::string str(160, '\0'); + if (brand_ == Brand::NO_BRAND) { + snprintf_P(&str[0], str.capacity() + 1, PSTR("%s: %s"), device_type_name().c_str(), name_.c_str()); + } else { + snprintf_P(&str[0], str.capacity() + 1, PSTR("%s: %s %s"), device_type_name().c_str(), brand_to_string().c_str(), name_.c_str()); + } + return str; +} + // prints the header for the section void EMSdevice::show_values(uuid::console::Shell & shell) { shell.printfln(F("%s: %s"), device_type_name().c_str(), to_string().c_str()); diff --git a/src/emsdevice.h b/src/emsdevice.h index 5030e1108..2abc3bc42 100644 --- a/src/emsdevice.h +++ b/src/emsdevice.h @@ -102,12 +102,22 @@ class EMSdevice { return name_; } + inline uint8_t unique_id() const { + return unique_id_; + } + + inline void unique_id(uint8_t unique_id) { + unique_id_ = unique_id; + } + std::string brand_to_string() const; static uint8_t decode_brand(uint8_t value); std::string to_string() const; - void show_telegram_handlers(uuid::console::Shell & shell); - void show_mqtt_handlers(uuid::console::Shell & shell); + std::string to_string_short() const; + + void show_telegram_handlers(uuid::console::Shell & shell); + void show_mqtt_handlers(uuid::console::Shell & shell); using process_function_p = std::function)>; void register_telegram_type(const uint16_t telegram_type_id, const __FlashStringHelper * telegram_type_name, bool fetch, process_function_p cb); @@ -126,6 +136,7 @@ class EMSdevice { virtual void publish_values() = 0; virtual bool updated_values() = 0; virtual void add_context_menu() = 0; + virtual void device_info(JsonArray & root) = 0; std::string telegram_type_name(std::shared_ptr telegram); @@ -165,6 +176,37 @@ class EMSdevice { } } + // takes a value from an ems device and creates a nested json (name, value) + // which can be passed to the web UI + template + static void render_value_json(JsonArray & json, + const std::string & prefix, + const __FlashStringHelper * name, + Value & value, + const __FlashStringHelper * suffix, + const uint8_t format = 0) { + char buffer[15]; + if (Helpers::render_value(buffer, value, format) == nullptr) { + return; + } + + JsonObject dataElement = json.createNestedObject(); + + // copy flash into std::strings to ensure arduinojson can reference them without a copy + + if (suffix != nullptr) { + std::string text(20, '\0'); + snprintf_P(&text[0], text.capacity() + 1, PSTR("%s%s"), buffer, uuid::read_flash_string(suffix).c_str()); + dataElement["value"] = text; + } else { + dataElement["value"] = buffer; + } + + std::string text2(100, '\0'); + snprintf_P(&text2[0], text2.capacity() + 1, PSTR("%s%s"), prefix.c_str(), uuid::read_flash_string(name).c_str()); + dataElement["name"] = text2; + } + static void print_value(uuid::console::Shell & shell, uint8_t padding, const __FlashStringHelper * name, const __FlashStringHelper * value); static void print_value(uuid::console::Shell & shell, uint8_t padding, const __FlashStringHelper * name, const char * value); @@ -229,6 +271,7 @@ class EMSdevice { static constexpr uint8_t EMS_DEVICE_FLAG_JUNKERS_2 = (1 << 6); // 6th bit set if older models private: + uint8_t unique_id_; uint8_t device_type_ = DeviceType::UNKNOWN; uint8_t device_id_ = 0; uint8_t product_id_ = 0; diff --git a/src/emsesp.cpp b/src/emsesp.cpp index a927dc573..b6e2b6e72 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -39,9 +39,8 @@ ESP8266React EMSESP::esp8266React(&webServer, &dummyFS); EMSESPSettingsService EMSESP::emsespSettingsService = EMSESPSettingsService(&webServer, &dummyFS, EMSESP::esp8266React.getSecurityManager()); #endif -EMSESPStatusService EMSESP::emsespStatusService = EMSESPStatusService(&webServer, EMSESP::esp8266React.getSecurityManager()); -EMSESPDevicesService EMSESP::emsespDevicesService = EMSESPDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager()); -EMSESPScanDevicesService EMSESP::emsespScanDevicesService = EMSESPScanDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager()); +EMSESPStatusService EMSESP::emsespStatusService = EMSESPStatusService(&webServer, EMSESP::esp8266React.getSecurityManager()); +EMSESPDevicesService EMSESP::emsespDevicesService = EMSESPDevicesService(&webServer, EMSESP::esp8266React.getSecurityManager()); std::vector> EMSESP::emsdevices; // array of all the detected EMS devices std::vector EMSESP::device_library_; // libary of all our known EMS devices so far @@ -63,6 +62,7 @@ uint16_t EMSESP::watch_id_ = WATCH_ID_NONE; / uint8_t EMSESP::watch_ = 0; // trace off bool EMSESP::tap_water_active_ = false; // for when Boiler states we having running warm water. used in Shower() uint32_t EMSESP::last_fetch_ = 0; +uint8_t EMSESP::unique_id_count_ = 0; // for a specific EMS device go and request data values // or if device_id is 0 it will fetch from all our known and active devices @@ -474,6 +474,20 @@ bool EMSESP::process_telegram(std::shared_ptr telegram) { return found; } +// calls the device handler's function to populate a json doc with device info +void EMSESP::device_info(const uint8_t unique_id, JsonObject & root) { + for (const auto & emsdevice : emsdevices) { + if (emsdevice) { + if (emsdevice->unique_id() == unique_id) { + root["deviceName"] = emsdevice->to_string_short(); // can;t use c_str() because of scope + JsonArray data = root.createNestedArray("deviceData"); + emsdevice->device_info(data); + return; + } + } + } +} + // return true if we have this device already registered bool EMSESP::device_exists(const uint8_t device_id) { for (const auto & emsdevice : emsdevices) { @@ -513,6 +527,9 @@ void EMSESP::show_devices(uuid::console::Shell & shell) { // shell.printf(F("[factory ID: %d] "), device_class.first); for (const auto & emsdevice : emsdevices) { if ((emsdevice) && (emsdevice->device_type() == device_class.first)) { +#if defined(EMSESP_DEBUG) + shell.printf(F("[id=%d] "), emsdevice->unique_id()); +#endif shell.printf(F("%s: %s"), emsdevice->device_type_name().c_str(), emsdevice->to_string().c_str()); if ((emsdevice->device_type() == EMSdevice::DeviceType::THERMOSTAT) && (emsdevice->device_id() == actual_master_thermostat())) { shell.printf(F(" ** master device **")); @@ -584,6 +601,7 @@ bool EMSESP::add_device(const uint8_t device_id, const uint8_t product_id, std:: } else { emsdevices.push_back( EMSFactory::add(device_p->device_type, device_id, device_p->product_id, version, uuid::read_flash_string(device_p->name), device_p->flags, brand)); + emsdevices.back()->unique_id(++unique_id_count_); LOG_DEBUG(F("Adding new device with device ID 0x%02X with product ID %d and version %s"), device_id, product_id, version.c_str()); // go and fetch its data, fetch_device_values(device_id); @@ -745,11 +763,11 @@ void EMSESP::loop() { } system_.loop(); // does LED and checks system health, and syslog service - mqtt_.loop(); // starts mqtt, and sends out anything in the queue rxservice_.loop(); // process what ever is in the rx queue txservice_.loop(); // check that the Tx is all ok shower_.loop(); // check for shower on/off sensors_.loop(); // this will also send out via MQTT + mqtt_.loop(); // sends out anything in the queue via MQTT console_.loop(); // telnet/serial console // force a query on the EMS devices to fetch latest data at a set interval (1 min) diff --git a/src/emsesp.h b/src/emsesp.h index 9829c002a..28d62c860 100644 --- a/src/emsesp.h +++ b/src/emsesp.h @@ -37,7 +37,6 @@ #include "EMSESPStatusService.h" #include "EMSESPDevicesService.h" #include "EMSESPSettingsService.h" -#include "EMSESPScanDevicesService.h" #include "emsdevice.h" #include "emsfactory.h" @@ -82,6 +81,8 @@ class EMSESP { static void send_raw_telegram(const char * data); static bool device_exists(const uint8_t device_id); + static void device_info(const uint8_t unique_id, JsonObject & root); + static uint8_t count_devices(const uint8_t device_type); static uint8_t actual_master_thermostat(); @@ -146,11 +147,10 @@ class EMSESP { static TxService txservice_; // web controllers - static ESP8266React esp8266React; - static EMSESPSettingsService emsespSettingsService; - static EMSESPStatusService emsespStatusService; - static EMSESPDevicesService emsespDevicesService; - static EMSESPScanDevicesService emsespScanDevicesService; + static ESP8266React esp8266React; + static EMSESPSettingsService emsespSettingsService; + static EMSESPStatusService emsespStatusService; + static EMSESPDevicesService emsespDevicesService; static uuid::log::Logger logger() { return logger_; @@ -182,6 +182,8 @@ class EMSESP { static uint16_t watch_id_; static uint8_t watch_; static bool tap_water_active_; + + static uint8_t unique_id_count_; }; } // namespace emsesp diff --git a/src/emsfactory.h b/src/emsfactory.h index ede08d8fe..50b2d0afe 100644 --- a/src/emsfactory.h +++ b/src/emsfactory.h @@ -89,6 +89,7 @@ class ConcreteEMSFactory : EMSFactory { ConcreteEMSFactory(const uint8_t device_type) { EMSFactory::registerFactory(device_type, this); } + auto construct(uint8_t device_type, uint8_t device_id, uint8_t product_id, std::string version, std::string name, uint8_t flags, uint8_t brand) const -> EMSdevice * { return new DerivedClass(device_type, device_id, product_id, version, name, flags, brand);