battery-minus

Pebble activity tracker that records battery events
git clone https://git.instinctive.eu/battery-minus.git
Log | Files | Refs | README | LICENSE

commit 320d03e021e806c61d799f1e5c01f22a3148555e
parent e4f981c456f910d32b25af311ae059e693eed92b
Author: Natasha Kerensikova <natacha@instinctive.eu>
Date:   Mon, 21 Dec 2015 18:27:41 +0000

Add basic code for the worker
Diffstat:
Asrc/storage.h | 47+++++++++++++++++++++++++++++++++++++++++++++++
Aworker_src/battery-minus_worker.c | 158+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 205 insertions(+), 0 deletions(-)

diff --git a/src/storage.h b/src/storage.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015, Natacha Porté + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef BATTERY_STORAGE_H +#define BATTERY_STORAGE_H + +/* a single entry in the event log */ +struct __attribute__((__packed__)) event { + time_t time; + uint8_t before; + uint8_t after; +}; + +/* + * after field has the following format: + * - the most significant is 1 when charging, 0 when discharging + * - the remaining 7 bits hold the battery percentage value + * + * before either the value before the event, using the same format as after, + * or one of the following special value: + * - UNKNOWN (update from anomalous to normal value) + * - APP_STARTED + * - APP_CLOSED + * - ANOMALOUS_VALUE (then after has the whole 8-bit value) + */ + +#define UNKNOWN 0xF0 +#define APP_STARTED 0xF1 +#define APP_CLOSED 0xF2 +#define ANOMALOUS_VALUE 0xF3 + +#define PAGE_LENGTH (PERSIST_DATA_MAX_LENGTH / sizeof(struct event)) + +#endif /* defined BATTERY_STORAGE_H */ diff --git a/worker_src/battery-minus_worker.c b/worker_src/battery-minus_worker.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2015, Natacha Porté + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <pebble_worker.h> + +#include "../src/storage.h" + +static struct event current_page[PAGE_LENGTH]; +static unsigned index; +static BatteryChargeState previous; + +/****************************** + * LOW LEVEL EVENT MANAGEMENT * + ******************************/ + +static void +append_event(struct event *event) { + int ret; + + if (index >= PAGE_LENGTH) { + APP_LOG(APP_LOG_LEVEL_ERROR, + "invalid value %u for index", index); + return; + } + + current_page[index] = *event; + index = (index + 1) % PAGE_LENGTH; + + ret = persist_write_data(1, current_page, sizeof current_page); + if (ret < 0 || (unsigned)ret != sizeof current_page) + APP_LOG(APP_LOG_LEVEL_ERROR, + "unexpected return value %d for persist_wride_data", + ret); +} + +static uint8_t +convert_state(BatteryChargeState *state) { + if (state->charge_percent > 100) return ANOMALOUS_VALUE; + return state->charge_percent | (state->is_charging ? 0x80 : 0); +} + +/********************* + * HIGH LEVEL EVENTS * + *********************/ + +static void +new_event(uint8_t before, uint8_t after) { + struct event event; + + event.time = time(0); + event.before = before; + event.after = after; + append_event(&event); +} + +static void +app_started(void) { + uint8_t current = convert_state(&previous); + + if (current == ANOMALOUS_VALUE) { + new_event(APP_STARTED, UNKNOWN); + new_event(ANOMALOUS_VALUE, previous.charge_percent); + } else + new_event(APP_STARTED, current); +} + +static void +app_stopped(void) { + uint8_t current = convert_state(&previous); + + new_event(APP_CLOSED, current == ANOMALOUS_VALUE ? UNKNOWN : current); +} + +static void +battery_update(BatteryChargeState *before, BatteryChargeState *after) { + uint8_t i_before = convert_state(before); + uint8_t i_after = convert_state(after); + + if (i_after == ANOMALOUS_VALUE) + new_event(ANOMALOUS_VALUE, after->charge_percent); + else + new_event(i_before == ANOMALOUS_VALUE ? UNKNOWN : i_before, + i_after); +} + +/***************** + * EVENT HANDLER * + *****************/ + +static void +battery_handler(BatteryChargeState charge) { + if (charge.charge_percent == previous.charge_percent + && charge.is_charging == previous.is_charging) + return; + + battery_update(&previous, &charge); + previous = charge; +} + +/*********************************** + * INITIALIZATION AND FINALIZATION * + ***********************************/ + +static bool +init(void) { + int ret = persist_read_data(1, current_page, sizeof current_page); + + if (ret == E_DOES_NOT_EXIST) { + APP_LOG(APP_LOG_LEVEL_INFO, + "no configuration found, initializing to zero"); + memset(current_page, 0, sizeof current_page); + } else if (ret != sizeof current_page) { + APP_LOG(APP_LOG_LEVEL_ERROR, + "unexpected return value %d for persist_read_data", + ret); + return false; + } else if (current_page[0].time) { + for (index = 1; + index < PAGE_LENGTH + && current_page[index - 1].time < current_page[index].time; + index += 1); + } else + index = 0; + + previous = battery_state_service_peek(); + app_started(); + + battery_state_service_subscribe(&battery_handler); + + return true; +} + +static void +deinit(void) { + battery_state_service_unsubscribe(); + app_stopped(); +} + +int +main(void) { + if (!init()) return 1; + worker_event_loop(); + deinit(); + return 0; +}