| /* |
| * |
| * BlueZ - Bluetooth protocol stack for Linux |
| * |
| * Copyright (C) 2017 Intel Corporation. All rights reserved. |
| * |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <stdbool.h> |
| #include <inttypes.h> |
| #include <stdbool.h> |
| #include <sys/uio.h> |
| #include <wordexp.h> |
| #include <readline/readline.h> |
| #include <readline/history.h> |
| #include <glib.h> |
| |
| #include "src/shared/shell.h" |
| #include "src/shared/util.h" |
| #include "mesh/mesh-net.h" |
| #include "mesh/keys.h" |
| #include "mesh/net.h" |
| #include "mesh/node.h" |
| #include "mesh/prov-db.h" |
| #include "mesh/util.h" |
| #include "mesh/onoff-model.h" |
| |
| static uint8_t trans_id; |
| static uint16_t onoff_app_idx = APP_IDX_INVALID; |
| |
| static int client_bind(uint16_t app_idx, int action) |
| { |
| if (action == ACTION_ADD) { |
| if (onoff_app_idx != APP_IDX_INVALID) { |
| return MESH_STATUS_INSUFF_RESOURCES; |
| } else { |
| onoff_app_idx = app_idx; |
| bt_shell_printf("On/Off client model: new binding" |
| " %4.4x\n", app_idx); |
| } |
| } else { |
| if (onoff_app_idx == app_idx) |
| onoff_app_idx = APP_IDX_INVALID; |
| } |
| return MESH_STATUS_SUCCESS; |
| } |
| |
| static void print_remaining_time(uint8_t remaining_time) |
| { |
| uint8_t step = (remaining_time & 0xc0) >> 6; |
| uint8_t count = remaining_time & 0x3f; |
| int secs = 0, msecs = 0, minutes = 0, hours = 0; |
| |
| switch (step) { |
| case 0: |
| msecs = 100 * count; |
| secs = msecs / 1000; |
| msecs -= (secs * 1000); |
| break; |
| case 1: |
| secs = 1 * count; |
| minutes = secs / 60; |
| secs -= (minutes * 60); |
| break; |
| |
| case 2: |
| secs = 10 * count; |
| minutes = secs / 60; |
| secs -= (minutes * 60); |
| break; |
| case 3: |
| minutes = 10 * count; |
| hours = minutes / 60; |
| minutes -= (hours * 60); |
| break; |
| |
| default: |
| break; |
| } |
| |
| bt_shell_printf("\n\t\tRemaining time: %d hrs %d mins %d secs %d" |
| " msecs\n", hours, minutes, secs, msecs); |
| |
| } |
| |
| static bool client_msg_recvd(uint16_t src, uint8_t *data, |
| uint16_t len, void *user_data) |
| { |
| uint32_t opcode; |
| int n; |
| |
| if (mesh_opcode_get(data, len, &opcode, &n)) { |
| len -= n; |
| data += n; |
| } else |
| return false; |
| |
| bt_shell_printf("On Off Model Message received (%d) opcode %x\n", |
| len, opcode); |
| print_byte_array("\t",data, len); |
| |
| switch (opcode & ~OP_UNRELIABLE) { |
| default: |
| return false; |
| |
| case OP_GENERIC_ONOFF_STATUS: |
| if (len != 1 && len != 3) |
| break; |
| |
| bt_shell_printf("Node %4.4x: Off Status present = %s", |
| src, data[0] ? "ON" : "OFF"); |
| |
| if (len == 3) { |
| bt_shell_printf(", target = %s", |
| data[1] ? "ON" : "OFF"); |
| print_remaining_time(data[2]); |
| } else |
| bt_shell_printf("\n"); |
| break; |
| } |
| |
| return true; |
| } |
| |
| |
| static uint32_t target; |
| static uint32_t parms[8]; |
| |
| static uint32_t read_input_parameters(int argc, char *argv[]) |
| { |
| uint32_t i; |
| |
| if (!argc) |
| return 0; |
| |
| --argc; |
| ++argv; |
| |
| if (!argc || argv[0][0] == '\0') |
| return 0; |
| |
| memset(parms, 0xff, sizeof(parms)); |
| |
| for (i = 0; i < sizeof(parms)/sizeof(parms[0]) && i < (unsigned) argc; |
| i++) { |
| sscanf(argv[i], "%x", &parms[i]); |
| if (parms[i] == 0xffffffff) |
| break; |
| } |
| |
| return i; |
| } |
| |
| static void cmd_set_node(int argc, char *argv[]) |
| { |
| uint32_t dst; |
| char *end; |
| |
| dst = strtol(argv[1], &end, 16); |
| if (end != (argv[1] + 4)) { |
| bt_shell_printf("Bad unicast address %s: " |
| "expected format 4 digit hex\n", argv[1]); |
| target = UNASSIGNED_ADDRESS; |
| return bt_shell_noninteractive_quit(EXIT_FAILURE); |
| } else { |
| bt_shell_printf("Controlling ON/OFF for node %4.4x\n", dst); |
| target = dst; |
| set_menu_prompt("on/off", argv[1]); |
| return bt_shell_noninteractive_quit(EXIT_SUCCESS); |
| } |
| } |
| |
| static bool send_cmd(uint8_t *buf, uint16_t len) |
| { |
| struct mesh_node *node = node_get_local_node(); |
| uint8_t ttl; |
| |
| if(!node) |
| return false; |
| |
| ttl = node_get_default_ttl(node); |
| |
| return net_access_layer_send(ttl, node_get_primary(node), |
| target, onoff_app_idx, buf, len); |
| } |
| |
| static void cmd_get_status(int argc, char *argv[]) |
| { |
| uint16_t n; |
| uint8_t msg[32]; |
| struct mesh_node *node; |
| |
| if (IS_UNASSIGNED(target)) { |
| bt_shell_printf("Destination not set\n"); |
| return bt_shell_noninteractive_quit(EXIT_FAILURE); |
| } |
| |
| node = node_find_by_addr(target); |
| |
| if (!node) |
| return; |
| |
| n = mesh_opcode_set(OP_GENERIC_ONOFF_GET, msg); |
| |
| if (!send_cmd(msg, n)) { |
| bt_shell_printf("Failed to send \"GENERIC ON/OFF GET\"\n"); |
| return bt_shell_noninteractive_quit(EXIT_FAILURE); |
| } |
| |
| return bt_shell_noninteractive_quit(EXIT_SUCCESS); |
| } |
| |
| static void cmd_set(int argc, char *argv[]) |
| { |
| uint16_t n; |
| uint8_t msg[32]; |
| struct mesh_node *node; |
| |
| if (IS_UNASSIGNED(target)) { |
| bt_shell_printf("Destination not set\n"); |
| return bt_shell_noninteractive_quit(EXIT_FAILURE); |
| } |
| |
| node = node_find_by_addr(target); |
| |
| if (!node) |
| return; |
| |
| if ((read_input_parameters(argc, argv) != 1) && |
| parms[0] != 0 && parms[0] != 1) { |
| bt_shell_printf("Bad arguments: Expecting \"0\" or \"1\"\n"); |
| return bt_shell_noninteractive_quit(EXIT_FAILURE); |
| } |
| |
| n = mesh_opcode_set(OP_GENERIC_ONOFF_SET, msg); |
| msg[n++] = parms[0]; |
| msg[n++] = trans_id++; |
| |
| if (!send_cmd(msg, n)) { |
| bt_shell_printf("Failed to send \"GENERIC ON/OFF SET\"\n"); |
| return bt_shell_noninteractive_quit(EXIT_FAILURE); |
| } |
| |
| return bt_shell_noninteractive_quit(EXIT_SUCCESS); |
| } |
| |
| static const struct bt_shell_menu onoff_menu = { |
| .name = "onoff", |
| .desc = "On/Off Model Submenu", |
| .entries = { |
| {"target", "<unicast>", cmd_set_node, |
| "Set node to configure"}, |
| {"get", NULL, cmd_get_status, |
| "Get ON/OFF status"}, |
| {"onoff", "<0/1>", cmd_set, |
| "Send \"SET ON/OFF\" command"}, |
| {} }, |
| }; |
| |
| static struct mesh_model_ops client_cbs = { |
| client_msg_recvd, |
| client_bind, |
| NULL, |
| NULL |
| }; |
| |
| bool onoff_client_init(uint8_t ele) |
| { |
| if (!node_local_model_register(ele, GENERIC_ONOFF_CLIENT_MODEL_ID, |
| &client_cbs, NULL)) |
| return false; |
| |
| bt_shell_add_submenu(&onoff_menu); |
| |
| return true; |
| } |